developer28 commited on
Commit
9518e76
Β·
verified Β·
1 Parent(s): 3644986

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +219 -700
app.py CHANGED
@@ -3,46 +3,6 @@ import tempfile
3
  import gradio as gr
4
  import re
5
  import sys
6
- import shutil
7
- import importlib.util
8
- import time
9
- import random
10
-
11
- def check_requirements():
12
- """Check if all required packages are installed and return status"""
13
- requirements_status = []
14
-
15
- packages = [
16
- ('gradio', 'gradio'),
17
- ('yt-dlp', 'yt_dlp'),
18
- ('openai-whisper', 'whisper'),
19
- ('torch', 'torch'),
20
- ('torchaudio', 'torchaudio'),
21
- ('numpy', 'numpy'),
22
- ('regex', 'regex'),
23
- ]
24
-
25
- for package_name, import_name in packages:
26
- try:
27
- spec = importlib.util.find_spec(import_name)
28
- if spec is None:
29
- requirements_status.append(f"❌ {package_name}: Not found")
30
- continue
31
-
32
- module = importlib.import_module(import_name)
33
- version = getattr(module, '__version__', 'Unknown version')
34
- requirements_status.append(f"βœ… {package_name}: {version}")
35
-
36
- except ImportError as e:
37
- requirements_status.append(f"❌ {package_name}: Import error - {str(e)}")
38
- except Exception as e:
39
- requirements_status.append(f"⚠️ {package_name}: Found but error - {str(e)}")
40
-
41
- # Add Python info
42
- requirements_status.append(f"\n🐍 Python: {sys.version}")
43
- requirements_status.append(f"πŸ“ Python executable: {sys.executable}")
44
-
45
- return "\n".join(requirements_status)
46
 
47
  # Try to import required packages with error handling
48
  try:
@@ -52,317 +12,175 @@ except ImportError as e:
52
  YT_DLP_AVAILABLE = False
53
  print(f"yt-dlp import error: {e}")
54
 
55
- # Try multiple whisper import methods
56
- WHISPER_AVAILABLE = False
57
- WHISPER_TYPE = None
58
-
59
  try:
60
  import whisper
61
  WHISPER_AVAILABLE = True
62
- WHISPER_TYPE = "openai-whisper"
63
- print("Using OpenAI Whisper")
64
  except ImportError as e:
65
- print(f"OpenAI Whisper import error: {e}")
66
- try:
67
- from transformers import pipeline
68
- WHISPER_AVAILABLE = True
69
- WHISPER_TYPE = "transformers"
70
- print("Using Transformers Whisper")
71
- except ImportError as e2:
72
- print(f"Transformers Whisper import error: {e2}")
73
 
74
  print(f"Python version: {sys.version}")
75
- print(f"Python executable: {sys.executable}")
76
  print(f"yt-dlp available: {YT_DLP_AVAILABLE}")
77
- print(f"whisper available: {WHISPER_AVAILABLE} (type: {WHISPER_TYPE})")
78
 
79
- def get_video_info(url, cookies_file_path=None):
80
- """Get video information without downloading"""
81
- if not YT_DLP_AVAILABLE:
82
- raise Exception("yt-dlp is not available.")
83
-
84
- ydl_opts = {
85
- 'quiet': True,
86
- 'no_warnings': True,
87
- 'extract_flat': False,
88
- 'skip_download': True,
89
- }
90
-
91
- if cookies_file_path and os.path.exists(cookies_file_path):
92
- ydl_opts['cookiefile'] = cookies_file_path
93
-
94
- with YoutubeDL(ydl_opts) as ydl:
95
- try:
96
- info = ydl.extract_info(url, download=False)
97
- return {
98
- 'title': info.get('title', 'Unknown'),
99
- 'duration': info.get('duration', 0),
100
- 'availability': info.get('availability', 'unknown'),
101
- 'live_status': info.get('live_status', 'unknown'),
102
- }
103
- except Exception as e:
104
- return {'error': str(e)}
105
 
106
- def download_audio(url, cookies_file_path=None):
107
- """Download audio from YouTube URL with enhanced error handling"""
108
  if not YT_DLP_AVAILABLE:
109
  raise Exception("yt-dlp is not available. Please check the installation.")
110
 
111
  try:
112
- # First, try to get video info
113
- video_info = get_video_info(url, cookies_file_path)
114
- if 'error' in video_info:
115
- raise Exception(f"Video info error: {video_info['error']}")
116
-
117
- print(f"Video title: {video_info.get('title', 'Unknown')}")
118
- print(f"Video duration: {video_info.get('duration', 0)} seconds")
119
- print(f"Video availability: {video_info.get('availability', 'unknown')}")
120
-
121
  # Create a temporary directory for downloads
122
  temp_dir = tempfile.mkdtemp()
123
  output_path = os.path.join(temp_dir, "audio")
124
 
125
- # Enhanced options for better compatibility
 
 
 
126
  ydl_opts = {
127
- 'format': 'bestaudio[ext=m4a]/bestaudio[ext=webm]/bestaudio[ext=mp4]/bestaudio/best',
128
  'outtmpl': output_path + '.%(ext)s',
129
- 'quiet': False, # Enable logging for debugging
130
- 'no_warnings': False,
131
- 'extractor_retries': 5,
132
- 'fragment_retries': 5,
133
- 'retry_sleep_functions': {'http': lambda n: min(2 ** n, 60)},
134
- 'socket_timeout': 30,
135
- 'http_chunk_size': 10485760, # 10MB chunks
136
- 'writeinfojson': False,
137
- 'writesubtitles': False,
138
- 'writeautomaticsub': False,
139
- 'geo_bypass': True,
140
- 'geo_bypass_country': 'US',
141
  'extract_flat': False,
142
  'ignoreerrors': False,
 
 
 
 
 
 
 
 
143
  }
144
 
145
- # Enhanced cookies and headers handling
146
- if cookies_file_path and os.path.exists(cookies_file_path):
147
- ydl_opts['cookiefile'] = cookies_file_path
148
- print(f"βœ… Using cookies file: {cookies_file_path}")
149
  else:
150
- print("⚠️ No cookies file - using enhanced headers")
151
-
152
- # Always add enhanced headers
153
- ydl_opts.update({
154
- 'user_agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
155
- 'referer': 'https://www.youtube.com/',
156
- 'headers': {
157
- 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
158
- 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',
159
- 'Accept-Language': 'en-US,en;q=0.9',
160
- 'Accept-Encoding': 'gzip, deflate, br',
161
- 'DNT': '1',
162
- 'Connection': 'keep-alive',
163
- 'Upgrade-Insecure-Requests': '1',
164
- 'Sec-Fetch-Dest': 'document',
165
- 'Sec-Fetch-Mode': 'navigate',
166
- 'Sec-Fetch-Site': 'none',
167
- 'Sec-Fetch-User': '?1',
168
- 'sec-ch-ua': '"Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"',
169
- 'sec-ch-ua-mobile': '?0',
170
- 'sec-ch-ua-platform': '"Windows"',
171
- }
172
- })
173
-
174
- # Add random delay to avoid rate limiting
175
- time.sleep(random.uniform(1, 3))
176
 
177
  with YoutubeDL(ydl_opts) as ydl:
178
- print(f"Attempting to download audio from: {url}")
179
- info_dict = ydl.extract_info(url, download=True)
 
 
 
 
 
 
 
 
 
 
 
180
 
181
  # Find the downloaded file
182
- for ext in ['.m4a', '.webm', '.mp4', '.mp3', '.aac', '.opus']:
183
  potential_file = output_path + ext
184
  if os.path.exists(potential_file):
185
- print(f"βœ… Audio downloaded: {potential_file}")
186
  return potential_file
187
 
188
- # If no file found, list directory contents for debugging
189
- print(f"Files in temp directory: {os.listdir(temp_dir)}")
190
- raise FileNotFoundError("Downloaded audio file not found")
191
 
192
  except Exception as e:
193
- error_msg = str(e).lower()
194
-
195
- # Provide specific error messages and solutions
196
- if "video unavailable" in error_msg or "content isn't available" in error_msg:
197
- raise Exception(f"""
198
- ❌ Video Access Error: The video is unavailable or restricted.
199
-
200
- Possible reasons:
201
- β€’ Video is private, unlisted, or deleted
202
- β€’ Video is geo-blocked in your region
203
- β€’ Video has age restrictions
204
- β€’ Video requires sign-in to view
205
- β€’ Copyright restrictions
206
-
207
- Solutions to try:
208
- 1. Verify the video URL is correct and accessible
209
- 2. Try a different public video
210
- 3. Check if the video works in your browser
211
- 4. If using a playlist URL, try the direct video URL instead
212
- 5. For age-restricted videos, ensure cookies are from a logged-in account
213
-
214
- Original error: {str(e)}
215
- """)
216
- elif "403" in error_msg or "forbidden" in error_msg:
217
- raise Exception(f"""
218
- ❌ Access Forbidden (403): YouTube blocked the request.
219
-
220
- Solutions:
221
- 1. **Upload fresh cookies.txt file** (most important)
222
- 2. Get cookies from a logged-in YouTube account
223
- 3. Try again after a few minutes (rate limiting)
224
- 4. Use a different network/VPN if possible
225
-
226
- How to get fresh cookies:
227
- β€’ Visit YouTube while logged in
228
- β€’ Use browser extension to export cookies
229
- β€’ Upload the newest cookies.txt file
230
-
231
- Original error: {str(e)}
232
- """)
233
- elif "429" in error_msg or "rate limit" in error_msg:
234
- raise Exception(f"""
235
- ❌ Rate Limited (429): Too many requests.
236
-
237
- Solutions:
238
- 1. Wait 10-15 minutes before trying again
239
- 2. Upload fresh cookies.txt file
240
- 3. Try a different video
241
- 4. Use a different network if possible
242
-
243
- Original error: {str(e)}
244
- """)
245
  else:
246
- raise Exception(f"Failed to download audio: {str(e)}")
247
 
248
  def transcribe_audio(file_path):
249
  """Transcribe audio file using Whisper"""
250
  if not WHISPER_AVAILABLE:
251
- raise Exception("OpenAI Whisper is not available. Please install it using: pip install openai-whisper")
252
 
253
  try:
254
- if WHISPER_TYPE == "openai-whisper":
255
- # Use OpenAI Whisper with more robust settings
256
- model = whisper.load_model("base") # Use base model for better accuracy
257
- result = model.transcribe(
258
- file_path,
259
- language="en", # Specify English for better performance
260
- task="transcribe",
261
- verbose=False,
262
- fp16=False, # Better compatibility
263
- temperature=0.0, # More deterministic
264
- )
265
- return result["text"]
266
-
267
- elif WHISPER_TYPE == "transformers":
268
- # Use Transformers Whisper
269
- from transformers import pipeline
270
- transcriber = pipeline(
271
- "automatic-speech-recognition",
272
- model="openai/whisper-base",
273
- device=-1 # Use CPU for better compatibility
274
- )
275
- result = transcriber(file_path, return_timestamps=False)
276
- return result["text"]
277
-
278
- else:
279
- raise Exception("No compatible Whisper installation found")
280
-
281
  except Exception as e:
282
  raise Exception(f"Failed to transcribe audio: {str(e)}")
283
 
284
- def extract_stock_info_enhanced(text):
285
- """Enhanced stock information extraction with better patterns"""
286
  try:
287
  stock_info = []
288
 
289
- # Enhanced patterns for stock information
290
- stock_patterns = {
291
- 'symbols': r'\b[A-Z]{2,5}\b(?=\s+(?:stock|shares|ticker|symbol|price|target|buy|sell))',
292
- 'prices': r'\$\d+(?:\.\d{1,2})?(?:\s*(?:per share|each|target|price))?',
293
- 'percentages': r'\d+(?:\.\d{1,2})?%',
294
- 'actions': r'\b(?:buy|sell|hold|long|short|bullish|bearish|target|stop loss|take profit|accumulate|distribute)\b',
295
- 'companies': r'\b[A-Z][a-zA-Z]+(?:\s+[A-Z][a-zA-Z]+){0,2}(?:\s+(?:Inc|Corp|Company|Ltd|LLC)\.?)?',
296
- 'market_terms': r'\b(?:earnings|revenue|profit|loss|growth|dividend|yield|PE ratio|market cap|volume)\b',
297
- }
298
-
299
- # Extract information
300
- symbols = re.findall(stock_patterns['symbols'], text, re.IGNORECASE)
301
- prices = re.findall(stock_patterns['prices'], text)
302
- percentages = re.findall(stock_patterns['percentages'], text)
303
- actions = re.findall(stock_patterns['actions'], text, re.IGNORECASE)
304
- companies = re.findall(stock_patterns['companies'], text)
305
- market_terms = re.findall(stock_patterns['market_terms'], text, re.IGNORECASE)
306
 
307
- # Format results
308
- result = "=== πŸ“Š EXTRACTED STOCK INFORMATION ===\n\n"
 
 
 
309
 
310
- if symbols:
311
- result += f"πŸ”€ **Stock Symbols Found**: {', '.join(set(symbols[:10]))}\n\n"
312
 
313
  if companies:
314
- filtered_companies = [c for c in set(companies) if len(c) > 3 and c.upper() not in ['THE', 'AND', 'FOR', 'WITH']]
315
- if filtered_companies:
316
- result += f"🏒 **Companies Mentioned**: {', '.join(filtered_companies[:10])}\n\n"
317
 
318
- if prices:
319
- result += f"πŸ’° **Price Mentions**: {', '.join(set(prices[:10]))}\n\n"
320
 
321
- if percentages:
322
- result += f"πŸ“ˆ **Percentage Mentions**: {', '.join(set(percentages[:10]))}\n\n"
323
 
324
  if actions:
325
- result += f"🎯 **Trading Actions**: {', '.join(set(actions[:10]))}\n\n"
326
 
327
- if market_terms:
328
- result += f"πŸ“Š **Market Terms**: {', '.join(set(market_terms[:10]))}\n\n"
329
-
330
- # Look for recommendation sentences
331
- sentences = [s.strip() for s in text.split('.') if s.strip()]
332
  recommendations = []
333
-
334
  for sentence in sentences:
335
- sentence_lower = sentence.lower()
336
- if any(action in sentence_lower for action in ['buy', 'sell', 'target', 'recommend', 'suggest']):
337
- if any(symbol in sentence for symbol in symbols[:5]) or any(price in sentence for price in prices[:3]):
338
- recommendations.append(sentence)
339
 
340
  if recommendations:
341
- result += "🎯 **Potential Recommendations**:\n"
342
- for i, rec in enumerate(recommendations[:5], 1):
343
- result += f"{i}. {rec}\n"
344
- result += "\n"
345
 
346
- # Add summary
347
- if not any([symbols, prices, actions, recommendations]):
348
- result += "⚠️ **No clear stock recommendations found**\n\n"
349
- result += "**Possible reasons:**\n"
350
- result += "β€’ Video doesn't contain stock/financial content\n"
351
- result += "β€’ Audio quality was poor for transcription\n"
352
- result += "β€’ Content is not in English\n"
353
- result += "β€’ General market discussion without specific recommendations\n"
354
- else:
355
- result += "βœ… **Analysis Complete** - Please verify all information independently!\n"
356
-
357
- result += "\n" + "="*50 + "\n"
358
- result += "⚠️ **DISCLAIMER**: This is automated extraction for educational purposes only.\n"
359
- result += "Always conduct your own research before making investment decisions!\n"
360
- result += "="*50
361
 
362
  return result
363
 
364
  except Exception as e:
365
- return f"❌ Error extracting stock info: {str(e)}"
366
 
367
  def cleanup_file(file_path):
368
  """Clean up temporary files"""
@@ -377,487 +195,188 @@ def cleanup_file(file_path):
377
  except:
378
  pass
379
 
380
- def process_cookies_file(cookies_file):
381
- """Process uploaded cookies file and return the path"""
382
- if cookies_file is None:
383
- return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
384
 
 
385
  try:
386
- # Create a temporary file for cookies
387
- temp_cookies_path = tempfile.mktemp(suffix='.txt')
388
-
389
- # Copy the uploaded file
390
- shutil.copy2(cookies_file, temp_cookies_path)
391
-
392
- # Validate cookies file
393
- with open(temp_cookies_path, 'r', encoding='utf-8') as f:
394
- content = f.read()
395
- if 'youtube.com' not in content.lower():
396
- print("⚠️ Warning: cookies file might not contain YouTube cookies")
397
-
398
- print(f"βœ… Cookies file processed: {temp_cookies_path}")
399
- return temp_cookies_path
400
  except Exception as e:
401
- print(f"❌ Error processing cookies file: {e}")
402
- return None
403
-
404
- def validate_youtube_url(url):
405
- """Validate YouTube URL format"""
406
- if not url or not url.strip():
407
- return False, "Please provide a YouTube URL"
408
-
409
- url = url.strip()
410
- youtube_patterns = [
411
- r'(?:https?://)?(?:www\.)?youtube\.com/watch\?v=[\w-]+',
412
- r'(?:https?://)?(?:www\.)?youtu\.be/[\w-]+',
413
- r'(?:https?://)?(?:www\.)?youtube\.com/embed/[\w-]+',
414
- r'(?:https?://)?(?:m\.)?youtube\.com/watch\?v=[\w-]+',
415
- ]
416
 
417
- for pattern in youtube_patterns:
418
- if re.match(pattern, url):
419
- return True, "Valid YouTube URL"
 
 
 
420
 
421
- return False, "Invalid YouTube URL format"
422
 
423
- def process_video(url, cookies_file, progress=gr.Progress()):
424
- """Main function to process YouTube video with detailed debugging"""
425
-
426
- # Detailed debugging info
427
- debug_info = []
428
- debug_info.append(f"πŸ” Starting process at {time.strftime('%H:%M:%S')}")
429
- debug_info.append(f"πŸ“‘ Python version: {sys.version.split()[0]}")
430
- debug_info.append(f"πŸ“¦ yt-dlp available: {YT_DLP_AVAILABLE}")
431
- debug_info.append(f"πŸŽ™οΈ Whisper available: {WHISPER_AVAILABLE} (type: {WHISPER_TYPE})")
432
 
433
  # Check if required packages are available
434
  if not YT_DLP_AVAILABLE:
435
- error_msg = "❌ ERROR: yt-dlp is not installed properly.\n\n"
436
- error_msg += "SOLUTION: Install yt-dlp using:\n"
437
- error_msg += "pip install yt-dlp\n\n"
438
- error_msg += "DEBUG INFO:\n" + "\n".join(debug_info)
439
- return error_msg, "", "❌ Missing yt-dlp"
440
 
441
  if not WHISPER_AVAILABLE:
442
- error_msg = "❌ ERROR: OpenAI Whisper is not installed properly.\n\n"
443
- error_msg += "SOLUTION: Install Whisper using:\n"
444
- error_msg += "pip install openai-whisper\n"
445
- error_msg += "OR\n"
446
- error_msg += "pip install transformers torch torchaudio\n\n"
447
- error_msg += "DEBUG INFO:\n" + "\n".join(debug_info)
448
- return error_msg, "", "❌ Missing Whisper"
449
 
450
- # Validate URL
451
- is_valid, validation_msg = validate_youtube_url(url)
452
- if not is_valid:
453
- error_msg = f"❌ ERROR: {validation_msg}\n\n"
454
- error_msg += f"PROVIDED URL: {url}\n\n"
455
- error_msg += "VALID URL FORMATS:\n"
456
- error_msg += "οΏ½οΏ½ https://www.youtube.com/watch?v=VIDEO_ID\n"
457
- error_msg += "β€’ https://youtu.be/VIDEO_ID\n"
458
- error_msg += "β€’ https://www.youtube.com/embed/VIDEO_ID\n\n"
459
- error_msg += "DEBUG INFO:\n" + "\n".join(debug_info)
460
- return error_msg, "", "❌ Invalid URL"
461
 
462
  audio_path = None
463
- cookies_temp_path = None
464
-
465
  try:
466
- progress(0.05, desc="πŸ” Validating URL...")
467
- debug_info.append(f"βœ… URL validation passed: {url}")
468
-
469
- # Process cookies file if provided
470
- progress(0.1, desc="πŸͺ Processing cookies...")
471
- cookies_temp_path = process_cookies_file(cookies_file)
472
-
473
- if cookies_temp_path:
474
- debug_info.append(f"βœ… Cookies processed: {cookies_temp_path}")
475
- else:
476
- debug_info.append("⚠️ No cookies provided - this may cause access errors")
477
-
478
- status_msg = "βœ… Cookies loaded" if cookies_temp_path else "⚠️ No cookies (may encounter restrictions)"
479
-
480
- # First, try to get video info for debugging
481
- progress(0.15, desc="πŸ” Checking video accessibility...")
482
- try:
483
- video_info = get_video_info(url, cookies_temp_path)
484
- if 'error' in video_info:
485
- debug_info.append(f"❌ Video info error: {video_info['error']}")
486
- raise Exception(f"Video accessibility check failed: {video_info['error']}")
487
- else:
488
- debug_info.append(f"βœ… Video info: {video_info}")
489
- except Exception as e:
490
- debug_info.append(f"❌ Video info check failed: {str(e)}")
491
- # Continue anyway, but log the issue
492
 
493
  # Download audio
494
- progress(0.2, desc="πŸ“₯ Downloading audio...")
495
- debug_info.append("πŸ”„ Starting audio download...")
496
- audio_path = download_audio(url, cookies_temp_path)
497
- debug_info.append(f"βœ… Audio downloaded: {audio_path}")
498
-
499
- # Check if audio file exists and get size
500
- if audio_path and os.path.exists(audio_path):
501
- file_size = os.path.getsize(audio_path)
502
- debug_info.append(f"πŸ“Š Audio file size: {file_size/1024/1024:.2f} MB")
503
- else:
504
- raise Exception("Audio file not found after download")
505
 
506
  # Transcribe audio
507
- progress(0.6, desc="πŸŽ™οΈ Transcribing audio...")
508
- debug_info.append("πŸ”„ Starting transcription...")
509
  transcript = transcribe_audio(audio_path)
510
- debug_info.append(f"βœ… Transcription completed: {len(transcript)} characters")
511
 
512
  if not transcript.strip():
513
- error_msg = "❌ ERROR: No speech detected in the video\n\n"
514
- error_msg += "POSSIBLE CAUSES:\n"
515
- error_msg += "β€’ Video has no audio track\n"
516
- error_msg += "β€’ Audio is too quiet or unclear\n"
517
- error_msg += "β€’ Video is not in English\n"
518
- error_msg += "β€’ Audio file is corrupted\n\n"
519
- error_msg += "DEBUG INFO:\n" + "\n".join(debug_info)
520
- return error_msg, "", "❌ No speech detected"
521
 
522
  # Extract stock information
523
- progress(0.9, desc="πŸ“Š Analyzing content...")
524
- debug_info.append("πŸ”„ Starting stock analysis...")
525
- stock_details = extract_stock_info_enhanced(transcript)
526
- debug_info.append("βœ… Stock analysis completed")
527
-
528
- progress(1.0, desc="βœ… Complete!")
529
 
530
- # Add debug info to transcript
531
- debug_section = "\n\n" + "="*50 + "\n"
532
- debug_section += "πŸ” DEBUG INFORMATION\n"
533
- debug_section += "="*50 + "\n"
534
- debug_section += "\n".join(debug_info)
535
-
536
- return transcript + debug_section, stock_details, "βœ… Processing completed successfully"
537
 
538
  except Exception as e:
539
- error_msg = f"❌ DETAILED ERROR INFORMATION:\n\n"
540
- error_msg += f"ERROR MESSAGE: {str(e)}\n\n"
541
- error_msg += f"ERROR TYPE: {type(e).__name__}\n\n"
542
-
543
- # Add context based on where the error occurred
544
- if "download" in str(e).lower():
545
- error_msg += "πŸ”§ DOWNLOAD TROUBLESHOOTING:\n"
546
- error_msg += "β€’ Check if video URL is accessible in browser\n"
547
- error_msg += "β€’ Upload fresh cookies.txt file\n"
548
- error_msg += "β€’ Try a different video\n"
549
- error_msg += "β€’ Wait 10-15 minutes if rate limited\n\n"
550
- elif "transcribe" in str(e).lower():
551
- error_msg += "πŸ”§ TRANSCRIPTION TROUBLESHOOTING:\n"
552
- error_msg += "β€’ Check if audio file was downloaded properly\n"
553
- error_msg += "β€’ Ensure video has clear audio\n"
554
- error_msg += "β€’ Try a shorter video\n\n"
555
-
556
- error_msg += "πŸ“Š PROCESSING STEPS COMPLETED:\n"
557
- error_msg += "\n".join(debug_info)
558
-
559
- return error_msg, "", f"❌ Error: {type(e).__name__}"
560
 
561
  finally:
562
  # Clean up temporary files
563
- if audio_path:
564
- debug_info.append(f"πŸ—‘οΈ Cleaning up: {audio_path}")
565
- cleanup_file(audio_path)
566
- if cookies_temp_path:
567
- debug_info.append(f"πŸ—‘οΈ Cleaning up: {cookies_temp_path}")
568
- cleanup_file(cookies_temp_path)
569
 
570
- # Create Gradio interface optimized for Gradio Cloud
571
  with gr.Blocks(
572
- title="πŸ“ˆ YouTube Stock Extractor",
573
  theme=gr.themes.Soft(),
574
  css="""
575
  .gradio-container {
576
  max-width: 1200px;
577
  margin: auto;
578
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
579
- }
580
- .status-box {
581
- padding: 12px;
582
- border-radius: 8px;
583
- margin: 10px 0;
584
- border: 1px solid #ddd;
585
- }
586
- .warning-box {
587
- background-color: #fff3cd;
588
- border-color: #ffeaa7;
589
- color: #856404;
590
- }
591
- .success-box {
592
- background-color: #d4edda;
593
- border-color: #c3e6cb;
594
- color: #155724;
595
- }
596
- .error-box {
597
- background-color: #f8d7da;
598
- border-color: #f5c6cb;
599
- color: #721c24;
600
  }
601
  """
602
  ) as demo:
603
 
604
  gr.Markdown("""
605
- # πŸ“ˆ YouTube Stock Recommendation Extractor
606
 
607
- **Extract stock analysis and trading recommendations from YouTube videos using AI**
608
 
609
- πŸ”§ **How it works:**
610
- 1. **Upload cookies.txt** (essential for avoiding restrictions)
611
- 2. **Paste YouTube URL** of financial content
612
- 3. **AI downloads** audio and transcribes using Whisper
613
- 4. **Extracts** stock symbols, prices, and recommendations
614
 
615
- ⚠️ **Important:** This tool is for educational purposes only. Always do your own research before investing!
616
  """)
617
 
 
 
 
 
 
 
 
 
 
 
 
618
  with gr.Row():
619
  with gr.Column(scale=1):
620
- # System check section
621
- with gr.Group():
622
- gr.Markdown("### πŸ” System Status")
623
- check_req_btn = gr.Button(
624
- "Check System Requirements",
625
- variant="secondary",
626
- size="sm"
627
- )
628
-
629
- requirements_output = gr.Textbox(
630
- label="πŸ“‹ System Requirements Status",
631
- lines=8,
632
- max_lines=15,
633
- interactive=False,
634
- visible=False
635
- )
636
 
637
- # Input section
638
- with gr.Group():
639
- gr.Markdown("### πŸ“₯ Input")
640
-
641
- # Add a test button first
642
- test_btn = gr.Button(
643
- "πŸ§ͺ Test System (Click First!)",
644
- variant="secondary",
645
- size="sm"
646
- )
647
-
648
- test_output = gr.Textbox(
649
- label="πŸ§ͺ System Test Results",
650
- lines=5,
651
- visible=False,
652
- interactive=False
653
- )
654
-
655
- # Cookies upload with better instructions
656
- cookies_input = gr.File(
657
- label="πŸͺ Upload Cookies File (cookies.txt) - HIGHLY RECOMMENDED",
658
- file_types=[".txt"],
659
- file_count="single"
660
- )
661
-
662
- with gr.Accordion("πŸ“‹ How to Get Cookies (Click to expand)", open=False):
663
- gr.Markdown("""
664
- **Why cookies are needed:** YouTube blocks most automated requests without proper authentication.
665
-
666
- **Step-by-step instructions:**
667
- 1. **Install browser extension:**
668
- - Chrome: "Get cookies.txt LOCALLY" or "cookies.txt"
669
- - Firefox: "cookies.txt" or "Export Cookies"
670
-
671
- 2. **Get cookies:**
672
- - Visit YouTube.com (log in if needed)
673
- - Click the extension icon
674
- - Select "Export for youtube.com"
675
- - Download the cookies.txt file
676
-
677
- 3. **Upload here:** Use the file upload above
678
-
679
- **⚠️ Without cookies, you'll get "403 Forbidden" or "Video unavailable" errors**
680
- """)
681
-
682
- url_input = gr.Textbox(
683
- label="πŸ“Ί YouTube Video URL",
684
- placeholder="https://www.youtube.com/watch?v=VIDEO_ID",
685
- lines=2,
686
- info="Paste the full YouTube video URL here"
687
- )
688
-
689
- process_btn = gr.Button(
690
- "πŸš€ Extract Stock Information",
691
- variant="primary",
692
- size="lg"
693
- )
694
-
695
- # Status display
696
- status_output = gr.Textbox(
697
- label="πŸ“Š Status",
698
- lines=3,
699
- interactive=False,
700
- info="Current processing status"
701
- )
702
 
703
- # Output section
704
  with gr.Row():
705
  with gr.Column():
706
  transcript_output = gr.Textbox(
707
  label="πŸ“ Full Transcript",
708
- lines=20,
709
- max_lines=25,
710
- show_copy_button=True,
711
- info="Complete transcription of the video audio"
712
  )
713
 
714
  with gr.Column():
715
  stock_info_output = gr.Textbox(
716
  label="πŸ“Š Extracted Stock Information",
717
- lines=20,
718
- max_lines=25,
719
- show_copy_button=True,
720
- info="Parsed stock symbols, prices, and recommendations"
721
  )
722
 
723
- # Example and troubleshooting section
724
- with gr.Row():
725
- with gr.Column():
726
- gr.Markdown("### πŸ“‹ Example URLs")
727
- gr.Examples(
728
- examples=[
729
- ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"],
730
- ["https://youtu.be/dQw4w9WgXcQ"],
731
- ],
732
- inputs=[url_input],
733
- label="Click to try example URLs (replace with actual financial videos)"
734
- )
735
-
736
- # Troubleshooting section
737
- with gr.Accordion("πŸ”§ Troubleshooting Guide", open=False):
738
- gr.Markdown("""
739
- ### Common Issues and Solutions:
740
-
741
- **❌ "Video unavailable" or "Content isn't available":**
742
- - Video might be private, deleted, or geo-blocked
743
- - Try a different public financial video
744
- - Verify the URL works in your browser
745
- - Check if video requires age verification
746
-
747
- **❌ "403 Forbidden" error:**
748
- - **Upload fresh cookies.txt file** (most common fix)
749
- - Make sure cookies are from a logged-in YouTube account
750
- - Try waiting 10-15 minutes (rate limiting)
751
-
752
- **❌ "No speech detected":**
753
- - Video might not have clear audio
754
- - Try videos with clear narration
755
- - Check if video is in English
756
-
757
- **❌ "No stock information found":**
758
- - Video might not contain financial content
759
- - Try videos from financial YouTube channels
760
- - Look for videos with stock analysis or recommendations
761
-
762
- ### Installation Commands:
763
- ```bash
764
- # Install all requirements
765
- pip install gradio yt-dlp openai-whisper torch torchaudio
766
-
767
- # Alternative whisper installation
768
- pip install transformers torch torchaudio
769
- ```
770
-
771
- ### Best Practices:
772
- - Use videos from reputable financial channels
773
- - Prefer videos under 20 minutes for faster processing
774
- - Ensure clear audio quality
775
- - Always verify extracted information independently
776
- """)
777
-
778
  # Event handlers
779
- def show_requirements():
780
- status = check_requirements()
781
- return gr.update(value=status, visible=True)
782
-
783
- def test_system():
784
- """Test system components and return detailed status"""
785
- test_results = []
786
- test_results.append("πŸ§ͺ SYSTEM TEST RESULTS")
787
- test_results.append("="*30)
788
-
789
- # Test imports
790
- test_results.append(f"βœ… yt-dlp: {'Available' if YT_DLP_AVAILABLE else 'NOT AVAILABLE'}")
791
- test_results.append(f"βœ… Whisper: {'Available' if WHISPER_AVAILABLE else 'NOT AVAILABLE'} (Type: {WHISPER_TYPE})")
792
-
793
- # Test yt-dlp functionality
794
- if YT_DLP_AVAILABLE:
795
- try:
796
- from yt_dlp import YoutubeDL
797
- test_ydl = YoutubeDL({'quiet': True})
798
- test_results.append("βœ… yt-dlp: Can create YoutubeDL instance")
799
- except Exception as e:
800
- test_results.append(f"❌ yt-dlp: Error creating instance - {str(e)}")
801
-
802
- # Test Whisper functionality
803
- if WHISPER_AVAILABLE:
804
- try:
805
- if WHISPER_TYPE == "openai-whisper":
806
- import whisper
807
- test_results.append("βœ… Whisper: OpenAI Whisper can be imported")
808
- elif WHISPER_TYPE == "transformers":
809
- from transformers import pipeline
810
- test_results.append("βœ… Whisper: Transformers Whisper can be imported")
811
- except Exception as e:
812
- test_results.append(f"❌ Whisper: Error testing - {str(e)}")
813
-
814
- # Test file operations
815
- try:
816
- temp_file = tempfile.mktemp()
817
- with open(temp_file, 'w') as f:
818
- f.write("test")
819
- os.remove(temp_file)
820
- test_results.append("βœ… File operations: Working")
821
- except Exception as e:
822
- test_results.append(f"❌ File operations: Error - {str(e)}")
823
-
824
- test_results.append("\nπŸ’‘ If you see any ❌ errors above, install missing packages:")
825
- test_results.append("pip install yt-dlp openai-whisper torch torchaudio")
826
-
827
- return gr.update(value="\n".join(test_results), visible=True)
828
-
829
- check_req_btn.click(
830
- fn=show_requirements,
831
- outputs=[requirements_output]
832
- )
833
-
834
- test_btn.click(
835
- fn=test_system,
836
- outputs=[test_output]
837
- )
838
-
839
  process_btn.click(
840
  fn=process_video,
841
- inputs=[url_input, cookies_input],
842
- outputs=[transcript_output, stock_info_output, status_output],
843
  show_progress=True
844
  )
845
 
846
- # Footer
847
- gr.Markdown("""
848
- ---
849
- **πŸ“’ Disclaimer:** This tool is for educational and research purposes only.
850
- The extracted information should not be considered as financial advice.
851
- Always conduct your own research and consult with financial professionals before making investment decisions.
852
- """)
 
 
853
 
854
- # Launch configuration for Gradio Cloud
855
  if __name__ == "__main__":
856
- demo.launch(
857
- server_name="0.0.0.0",
858
- server_port=7860,
859
- share=False,
860
- debug=False,
861
- show_error=True,
862
- quiet=False
863
- )
 
3
  import gradio as gr
4
  import re
5
  import sys
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
 
7
  # Try to import required packages with error handling
8
  try:
 
12
  YT_DLP_AVAILABLE = False
13
  print(f"yt-dlp import error: {e}")
14
 
 
 
 
 
15
  try:
16
  import whisper
17
  WHISPER_AVAILABLE = True
 
 
18
  except ImportError as e:
19
+ WHISPER_AVAILABLE = False
20
+ print(f"whisper import error: {e}")
 
 
 
 
 
 
21
 
22
  print(f"Python version: {sys.version}")
 
23
  print(f"yt-dlp available: {YT_DLP_AVAILABLE}")
24
+ print(f"whisper available: {WHISPER_AVAILABLE}")
25
 
26
+ def get_cookies_path():
27
+ """Get the path to cookies.txt file"""
28
+ # Check if cookies.txt exists in the current directory
29
+ if os.path.exists('cookies.txt'):
30
+ return 'cookies.txt'
31
+ # Check in the same directory as the script
32
+ script_dir = os.path.dirname(os.path.abspath(__file__))
33
+ cookies_path = os.path.join(script_dir, 'cookies.txt')
34
+ if os.path.exists(cookies_path):
35
+ return cookies_path
36
+ return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
 
38
+ def download_audio(url):
39
+ """Download audio from YouTube URL and return the file path"""
40
  if not YT_DLP_AVAILABLE:
41
  raise Exception("yt-dlp is not available. Please check the installation.")
42
 
43
  try:
 
 
 
 
 
 
 
 
 
44
  # Create a temporary directory for downloads
45
  temp_dir = tempfile.mkdtemp()
46
  output_path = os.path.join(temp_dir, "audio")
47
 
48
+ # Get cookies path
49
+ cookies_path = get_cookies_path()
50
+
51
+ # Base yt-dlp options
52
  ydl_opts = {
53
+ 'format': 'bestaudio[ext=m4a]/bestaudio/best',
54
  'outtmpl': output_path + '.%(ext)s',
55
+ 'quiet': True,
56
+ 'no_warnings': True,
 
 
 
 
 
 
 
 
 
 
57
  'extract_flat': False,
58
  'ignoreerrors': False,
59
+ # Add user agent to avoid bot detection
60
+ 'http_headers': {
61
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
62
+ },
63
+ # Add additional options to avoid bot detection
64
+ 'extractor_retries': 3,
65
+ 'fragment_retries': 3,
66
+ 'retry_sleep_functions': {'http': lambda n: 2 ** n},
67
  }
68
 
69
+ # Add cookies if available
70
+ if cookies_path:
71
+ ydl_opts['cookiefile'] = cookies_path
72
+ print(f"Using cookies from: {cookies_path}")
73
  else:
74
+ print("No cookies.txt found - proceeding without cookies")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
 
76
  with YoutubeDL(ydl_opts) as ydl:
77
+ # Extract info first to check if video is available
78
+ info_dict = ydl.extract_info(url, download=False)
79
+
80
+ # Check if video is available
81
+ if info_dict.get('availability') == 'private':
82
+ raise Exception("Video is private")
83
+ elif info_dict.get('availability') == 'premium_only':
84
+ raise Exception("Video requires premium subscription")
85
+ elif info_dict.get('live_status') == 'is_live':
86
+ raise Exception("Cannot download live streams")
87
+
88
+ # Download the audio
89
+ ydl.download([url])
90
 
91
  # Find the downloaded file
92
+ for ext in ['.m4a', '.webm', '.mp4', '.mp3']:
93
  potential_file = output_path + ext
94
  if os.path.exists(potential_file):
95
+ print(f"Successfully downloaded: {potential_file}")
96
  return potential_file
97
 
98
+ raise FileNotFoundError(f"Downloaded audio file not found")
 
 
99
 
100
  except Exception as e:
101
+ error_msg = str(e)
102
+ if "Sign in to confirm your age" in error_msg:
103
+ raise Exception("Video is age-restricted. Please use a different video or update your cookies.")
104
+ elif "Private video" in error_msg:
105
+ raise Exception("Video is private and cannot be accessed.")
106
+ elif "This video is unavailable" in error_msg:
107
+ raise Exception("Video is unavailable or has been removed.")
108
+ elif "blocked" in error_msg.lower():
109
+ raise Exception("Access to this video is blocked. Try using updated cookies or a different video.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  else:
111
+ raise Exception(f"Failed to download audio: {error_msg}")
112
 
113
  def transcribe_audio(file_path):
114
  """Transcribe audio file using Whisper"""
115
  if not WHISPER_AVAILABLE:
116
+ raise Exception("OpenAI Whisper is not available. Please check the installation.")
117
 
118
  try:
119
+ # Use the smallest model to reduce memory usage
120
+ model = whisper.load_model("tiny")
121
+ result = model.transcribe(file_path)
122
+ return result["text"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
  except Exception as e:
124
  raise Exception(f"Failed to transcribe audio: {str(e)}")
125
 
126
+ def extract_stock_info_simple(text):
127
+ """Extract stock information using simple pattern matching"""
128
  try:
129
  stock_info = []
130
 
131
+ # Simple patterns to look for stock-related information
132
+ stock_patterns = [
133
+ r'\b[A-Z]{1,5}\b(?:\s+stock|\s+shares|\s+symbol)', # Stock symbols
134
+ r'(?:buy|sell|target|price)\s+[A-Z]{1,5}',
135
+ r'\$\d+(?:\.\d{2})?', # Dollar amounts
136
+ r'\b(?:bullish|bearish|buy|sell|hold)\b',
137
+ ]
 
 
 
 
 
 
 
 
 
 
138
 
139
+ # Look for company names and stock mentions
140
+ companies = re.findall(r'\b[A-Z][a-z]+(?:\s+[A-Z][a-z]+)*(?:\s+(?:Inc|Corp|Company|Ltd)\.?)?', text)
141
+ symbols = re.findall(r'\b[A-Z]{2,5}\b', text)
142
+ prices = re.findall(r'\$\d+(?:\.\d{2})?', text)
143
+ actions = re.findall(r'\b(?:buy|sell|hold|bullish|bearish|target|stop\s+loss)\b', text, re.IGNORECASE)
144
 
145
+ # Format the extracted information
146
+ result = "=== EXTRACTED STOCK INFORMATION ===\n\n"
147
 
148
  if companies:
149
+ result += f"πŸ“Š Mentioned Companies: {', '.join(set(companies[:10]))}\n\n"
 
 
150
 
151
+ if symbols:
152
+ result += f"πŸ”€ Potential Stock Symbols: {', '.join(set(symbols[:10]))}\n\n"
153
 
154
+ if prices:
155
+ result += f"πŸ’° Price Mentions: {', '.join(set(prices[:10]))}\n\n"
156
 
157
  if actions:
158
+ result += f"πŸ“ˆ Trading Actions: {', '.join(set(actions[:10]))}\n\n"
159
 
160
+ # Look for specific recommendation patterns
 
 
 
 
161
  recommendations = []
162
+ sentences = text.split('.')
163
  for sentence in sentences:
164
+ if any(word in sentence.lower() for word in ['buy', 'sell', 'target', 'recommendation']):
165
+ if any(symbol in sentence for symbol in symbols[:5]):
166
+ recommendations.append(sentence.strip())
 
167
 
168
  if recommendations:
169
+ result += "🎯 Potential Recommendations:\n"
170
+ for rec in recommendations[:5]:
171
+ result += f"β€’ {rec}\n"
 
172
 
173
+ if not any([companies, symbols, prices, actions]):
174
+ result += "⚠️ No clear stock recommendations found in the transcript.\n"
175
+ result += "This might be because:\n"
176
+ result += "β€’ The video doesn't contain stock recommendations\n"
177
+ result += "β€’ The audio quality was poor\n"
178
+ result += "β€’ The content is not in English\n"
 
 
 
 
 
 
 
 
 
179
 
180
  return result
181
 
182
  except Exception as e:
183
+ return f"Error extracting stock info: {str(e)}"
184
 
185
  def cleanup_file(file_path):
186
  """Clean up temporary files"""
 
195
  except:
196
  pass
197
 
198
+ def system_test():
199
+ """Test system components"""
200
+ results = []
201
+
202
+ # Test yt-dlp
203
+ if YT_DLP_AVAILABLE:
204
+ results.append("βœ… yt-dlp: Available")
205
+ try:
206
+ ydl = YoutubeDL({'quiet': True})
207
+ results.append("βœ… yt-dlp: Can create YoutubeDL instance")
208
+ except Exception as e:
209
+ results.append(f"❌ yt-dlp: Cannot create instance - {e}")
210
+ else:
211
+ results.append("❌ yt-dlp: Not available")
212
+
213
+ # Test Whisper
214
+ if WHISPER_AVAILABLE:
215
+ results.append("βœ… Whisper: Available (Type: openai-whisper)")
216
+ try:
217
+ import whisper
218
+ results.append("βœ… Whisper: OpenAI Whisper can be imported")
219
+ except Exception as e:
220
+ results.append(f"❌ Whisper: Cannot import - {e}")
221
+ else:
222
+ results.append("❌ Whisper: Not available")
223
 
224
+ # Test file operations
225
  try:
226
+ temp_file = tempfile.NamedTemporaryFile(delete=False)
227
+ temp_file.write(b"test")
228
+ temp_file.close()
229
+ os.remove(temp_file.name)
230
+ results.append("βœ… File operations: Working")
 
 
 
 
 
 
 
 
 
231
  except Exception as e:
232
+ results.append(f"❌ File operations: Failed - {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
233
 
234
+ # Test cookies
235
+ cookies_path = get_cookies_path()
236
+ if cookies_path:
237
+ results.append(f"βœ… Cookies: Found at {cookies_path}")
238
+ else:
239
+ results.append("⚠️ Cookies: Not found (may cause bot detection issues)")
240
 
241
+ return "\n".join(results)
242
 
243
+ def process_video(url, progress=gr.Progress()):
244
+ """Main function to process YouTube video"""
 
 
 
 
 
 
 
245
 
246
  # Check if required packages are available
247
  if not YT_DLP_AVAILABLE:
248
+ return "Error: yt-dlp is not installed properly. Please check the requirements.", ""
 
 
 
 
249
 
250
  if not WHISPER_AVAILABLE:
251
+ return "Error: OpenAI Whisper is not installed properly. Please check the requirements.", ""
 
 
 
 
 
 
252
 
253
+ if not url or not url.strip():
254
+ return "Please provide a valid YouTube URL", ""
 
 
 
 
 
 
 
 
 
255
 
256
  audio_path = None
 
 
257
  try:
258
+ # Validate URL
259
+ if not any(domain in url.lower() for domain in ['youtube.com', 'youtu.be']):
260
+ return "Please provide a valid YouTube URL", ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
261
 
262
  # Download audio
263
+ progress(0.1, desc="Downloading audio...")
264
+ audio_path = download_audio(url)
 
 
 
 
 
 
 
 
 
265
 
266
  # Transcribe audio
267
+ progress(0.5, desc="Transcribing audio...")
 
268
  transcript = transcribe_audio(audio_path)
 
269
 
270
  if not transcript.strip():
271
+ return "No speech detected in the video", ""
 
 
 
 
 
 
 
272
 
273
  # Extract stock information
274
+ progress(0.8, desc="Extracting stock information...")
275
+ stock_details = extract_stock_info_simple(transcript)
 
 
 
 
276
 
277
+ progress(1.0, desc="Complete!")
278
+ return transcript, stock_details
 
 
 
 
 
279
 
280
  except Exception as e:
281
+ error_msg = f"Error processing video: {str(e)}"
282
+ return error_msg, ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
283
 
284
  finally:
285
  # Clean up temporary files
286
+ cleanup_file(audio_path)
 
 
 
 
 
287
 
288
+ # Create Gradio interface
289
  with gr.Blocks(
290
+ title="Stock Recommendation Extractor",
291
  theme=gr.themes.Soft(),
292
  css="""
293
  .gradio-container {
294
  max-width: 1200px;
295
  margin: auto;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
296
  }
297
  """
298
  ) as demo:
299
 
300
  gr.Markdown("""
301
+ # πŸ“ˆ Stock Recommendation Extractor from YouTube
302
 
303
+ Extract stock recommendations and trading information from YouTube videos using AI transcription.
304
 
305
+ **How it works:**
306
+ 1. Downloads audio from YouTube video
307
+ 2. Transcribes using OpenAI Whisper
308
+ 3. Extracts stock-related information
 
309
 
310
+ **⚠️ Disclaimer:** This is for educational purposes only. Always do your own research!
311
  """)
312
 
313
+ # Add system test section
314
+ with gr.Accordion("πŸ§ͺ System Status", open=False):
315
+ system_status = gr.Textbox(
316
+ value=system_test(),
317
+ label="System Test Results",
318
+ lines=10,
319
+ interactive=False
320
+ )
321
+ test_btn = gr.Button("πŸ”„ Re-run System Test")
322
+ test_btn.click(fn=system_test, outputs=system_status)
323
+
324
  with gr.Row():
325
  with gr.Column(scale=1):
326
+ url_input = gr.Textbox(
327
+ label="πŸ“Ί YouTube URL",
328
+ placeholder="https://www.youtube.com/watch?v=...",
329
+ lines=2
330
+ )
 
 
 
 
 
 
 
 
 
 
 
331
 
332
+ process_btn = gr.Button(
333
+ "πŸš€ Extract Stock Information",
334
+ variant="primary",
335
+ size="lg"
336
+ )
337
+
338
+ gr.Markdown("""
339
+ ### πŸ’‘ Tips:
340
+ - Works best with financial YouTube channels
341
+ - Ensure video has clear audio
342
+ - English content works best
343
+ - If you get bot detection errors, try updating cookies.txt
344
+ """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
345
 
 
346
  with gr.Row():
347
  with gr.Column():
348
  transcript_output = gr.Textbox(
349
  label="πŸ“ Full Transcript",
350
+ lines=15,
351
+ max_lines=20,
352
+ show_copy_button=True
 
353
  )
354
 
355
  with gr.Column():
356
  stock_info_output = gr.Textbox(
357
  label="πŸ“Š Extracted Stock Information",
358
+ lines=15,
359
+ max_lines=20,
360
+ show_copy_button=True
 
361
  )
362
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
363
  # Event handlers
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
364
  process_btn.click(
365
  fn=process_video,
366
+ inputs=[url_input],
367
+ outputs=[transcript_output, stock_info_output],
368
  show_progress=True
369
  )
370
 
371
+ # Example section
372
+ gr.Markdown("### πŸ“‹ Example URLs (Replace with actual financial videos)")
373
+ gr.Examples(
374
+ examples=[
375
+ ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"],
376
+ ],
377
+ inputs=[url_input],
378
+ label="Click to try example"
379
+ )
380
 
 
381
  if __name__ == "__main__":
382
+ demo.launch()