developer28 commited on
Commit
bb84391
Β·
verified Β·
1 Parent(s): a204567

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +94 -461
app.py CHANGED
@@ -1,512 +1,145 @@
 
 
 
 
1
  import os
2
  import tempfile
3
- import gradio as gr
4
- import re
5
- import sys
6
  import shutil
7
- import importlib.util
8
-
9
- def check_requirements():
10
- """Check if all required packages are installed and return status"""
11
- requirements_status = []
12
-
13
- packages = [
14
- ('gradio', 'gradio'),
15
- ('yt-dlp', 'yt_dlp'),
16
- ('openai-whisper', 'whisper'),
17
- ('torch', 'torch'),
18
- ('torchaudio', 'torchaudio'),
19
- ('numpy', 'numpy'),
20
- ('regex', 'regex'),
21
- ]
22
-
23
- for package_name, import_name in packages:
24
- try:
25
- spec = importlib.util.find_spec(import_name)
26
- if spec is None:
27
- requirements_status.append(f"❌ {package_name}: Not found")
28
- continue
29
-
30
- module = importlib.import_module(import_name)
31
- version = getattr(module, '__version__', 'Unknown version')
32
- requirements_status.append(f"βœ… {package_name}: {version}")
33
-
34
- except ImportError as e:
35
- requirements_status.append(f"❌ {package_name}: Import error - {str(e)}")
36
- except Exception as e:
37
- requirements_status.append(f"⚠️ {package_name}: Found but error - {str(e)}")
38
-
39
- # Add Python info
40
- requirements_status.append(f"\n🐍 Python: {sys.version}")
41
- requirements_status.append(f"πŸ“ Python executable: {sys.executable}")
42
-
43
- return "\n".join(requirements_status)
44
-
45
- # Try to import required packages with error handling
46
- try:
47
- from yt_dlp import YoutubeDL
48
- YT_DLP_AVAILABLE = True
49
- except ImportError as e:
50
- YT_DLP_AVAILABLE = False
51
- print(f"yt-dlp import error: {e}")
52
 
53
- # Try multiple whisper import methods
54
  WHISPER_AVAILABLE = False
55
  WHISPER_TYPE = None
56
-
57
  try:
58
  import whisper
59
  WHISPER_AVAILABLE = True
60
  WHISPER_TYPE = "openai-whisper"
61
- print("Using OpenAI Whisper")
62
- except ImportError as e:
63
- print(f"OpenAI Whisper import error: {e}")
64
  try:
65
  from transformers import pipeline
66
  WHISPER_AVAILABLE = True
67
  WHISPER_TYPE = "transformers"
68
- print("Using Transformers Whisper")
69
- except ImportError as e2:
70
- print(f"Transformers Whisper import error: {e2}")
71
-
72
- print(f"Python version: {sys.version}")
73
- print(f"Python executable: {sys.executable}")
74
- print(f"yt-dlp available: {YT_DLP_AVAILABLE}")
75
- print(f"whisper available: {WHISPER_AVAILABLE} (type: {WHISPER_TYPE})")
76
-
77
- # Additional diagnostics
78
- if YT_DLP_AVAILABLE:
79
- try:
80
- from yt_dlp import YoutubeDL
81
- print(f"yt-dlp version: {YoutubeDL().__class__.__module__}")
82
- except:
83
- pass
84
-
85
- if WHISPER_AVAILABLE and WHISPER_TYPE == "openai-whisper":
86
- try:
87
- import whisper
88
- print(f"whisper version: {whisper.__version__}")
89
- except:
90
  pass
91
 
92
- def download_audio(url, cookies_file_path=None):
93
- """Download audio from YouTube URL and return the file path"""
94
- if not YT_DLP_AVAILABLE:
95
- raise Exception("yt-dlp is not available. Please check the installation.")
96
-
97
- try:
98
- # Create a temporary directory for downloads
99
- temp_dir = tempfile.mkdtemp()
100
- output_path = os.path.join(temp_dir, "audio")
101
-
102
- # Base yt-dlp options
103
- ydl_opts = {
104
- 'format': 'bestaudio[ext=m4a]/bestaudio/best',
105
- 'outtmpl': output_path + '.%(ext)s',
106
- 'quiet': True,
107
- 'no_warnings': True,
108
- 'force_ipv4': True,
109
- 'referer': 'https://www.youtube.com/',
110
- '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',
111
- 'extractor_retries': 3,
112
- 'fragment_retries': 3,
113
- 'retry_sleep_functions': {'http': lambda n: 2 ** n},
114
- }
115
-
116
- # Add cookies file if provided
117
- if cookies_file_path and os.path.exists(cookies_file_path):
118
- print(f"βœ… Using cookies file: {cookies_file_path}")
119
- ydl_opts['cookiefile'] = cookies_file_path
120
- else:
121
- print("⚠️ No valid cookies file provided – likely to hit 403 Forbidden.")
122
-
123
- # Extra headers to mimic real browser
124
- ydl_opts['http_headers'] = {
125
- 'User-Agent': ydl_opts['user_agent'],
126
- 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
127
- 'Accept-Language': 'en-US,en;q=0.5',
128
- 'Accept-Encoding': 'gzip, deflate',
129
- 'DNT': '1',
130
- 'Connection': 'keep-alive',
131
- 'Upgrade-Insecure-Requests': '1',
132
- 'Referer': 'https://www.youtube.com/',
133
- }
134
-
135
- print(f"πŸ”§ yt-dlp options:\n{ydl_opts}")
136
-
137
- with YoutubeDL(ydl_opts) as ydl:
138
- info_dict = ydl.extract_info(url, download=True)
139
- filename = ydl.prepare_filename(info_dict)
140
-
141
- # Search for the downloaded audio file
142
- for ext in ['.m4a', '.webm', '.mp4', '.mp3']:
143
- potential_file = output_path + ext
144
- if os.path.exists(potential_file):
145
- print(f"βœ… Audio file downloaded: {potential_file}")
146
- return potential_file
147
-
148
- raise FileNotFoundError("Downloaded audio file not found.")
149
-
150
- except Exception as e:
151
- import traceback
152
- traceback.print_exc() # For debugging
153
- if "403" in str(e) or "Forbidden" in str(e):
154
- raise Exception(f"YouTube blocked the request (403 Forbidden). Please upload a valid cookies.txt file. Original error: {str(e)}")
155
- else:
156
- raise Exception(f"Failed to download audio: {str(e)}")
157
-
158
- def transcribe_audio(file_path):
159
- """Transcribe audio file using Whisper"""
160
- if not WHISPER_AVAILABLE:
161
- raise Exception("OpenAI Whisper is not available. Please install it using: pip install openai-whisper")
162
-
163
- try:
164
- if WHISPER_TYPE == "openai-whisper":
165
- # Use OpenAI Whisper
166
- model = whisper.load_model("tiny")
167
- result = model.transcribe(file_path)
168
- return result["text"]
169
-
170
- elif WHISPER_TYPE == "transformers":
171
- # Use Transformers Whisper
172
- from transformers import pipeline
173
- transcriber = pipeline("automatic-speech-recognition", model="openai/whisper-tiny")
174
- result = transcriber(file_path)
175
- return result["text"]
176
-
177
- else:
178
- raise Exception("No compatible Whisper installation found")
179
-
180
- except Exception as e:
181
- raise Exception(f"Failed to transcribe audio: {str(e)}")
182
-
183
 
184
  def extract_stock_info_simple(text):
185
- """Extract stock information using simple pattern matching"""
186
  try:
187
  stock_info = []
188
-
189
- # Simple patterns to look for stock-related information
190
- stock_patterns = [
191
- r'\b[A-Z]{1,5}\b(?:\s+stock|\s+shares|\s+symbol)', # Stock symbols
192
- r'(?:buy|sell|target|price)\s+[A-Z]{1,5}',
193
- r'\$\d+(?:\.\d{2})?', # Dollar amounts
194
- r'\b(?:bullish|bearish|buy|sell|hold)\b',
195
- ]
196
-
197
- # Look for company names and stock mentions
198
  companies = re.findall(r'\b[A-Z][a-z]+(?:\s+[A-Z][a-z]+)*(?:\s+(?:Inc|Corp|Company|Ltd)\.?)?', text)
199
  symbols = re.findall(r'\b[A-Z]{2,5}\b', text)
200
  prices = re.findall(r'\$\d+(?:\.\d{2})?', text)
201
- actions = re.findall(r'\b(?:buy|sell|hold|bullish|bearish|target|stop\s+loss)\b', text, re.IGNORECASE)
202
-
203
- # Format the extracted information
204
  result = "=== EXTRACTED STOCK INFORMATION ===\n\n"
205
-
206
  if companies:
207
- result += f"πŸ“Š Mentioned Companies: {', '.join(set(companies[:10]))}\n\n"
208
-
209
  if symbols:
210
- result += f"πŸ”€ Potential Stock Symbols: {', '.join(set(symbols[:10]))}\n\n"
211
-
212
  if prices:
213
- result += f"πŸ’° Price Mentions: {', '.join(set(prices[:10]))}\n\n"
214
-
215
  if actions:
216
- result += f"πŸ“ˆ Trading Actions: {', '.join(set(actions[:10]))}\n\n"
217
-
218
- # Look for specific recommendation patterns
219
  recommendations = []
220
  sentences = text.split('.')
221
  for sentence in sentences:
222
- if any(word in sentence.lower() for word in ['buy', 'sell', 'target', 'recommendation']):
223
- if any(symbol in sentence for symbol in symbols[:5]):
224
  recommendations.append(sentence.strip())
225
-
226
  if recommendations:
227
- result += "🎯 Potential Recommendations:\n"
228
  for rec in recommendations[:5]:
229
  result += f"β€’ {rec}\n"
230
-
231
  if not any([companies, symbols, prices, actions]):
232
- result += "⚠️ No clear stock recommendations found in the transcript.\n"
233
- result += "This might be because:\n"
234
- result += "β€’ The video doesn't contain stock recommendations\n"
235
- result += "β€’ The audio quality was poor\n"
236
- result += "β€’ The content is not in English\n"
237
-
238
  return result
239
-
240
  except Exception as e:
241
  return f"Error extracting stock info: {str(e)}"
242
 
243
- def cleanup_file(file_path):
244
- """Clean up temporary files"""
245
- try:
246
- if file_path and os.path.exists(file_path):
247
- os.remove(file_path)
248
- # Also try to remove the directory if it's empty
249
- try:
250
- os.rmdir(os.path.dirname(file_path))
251
- except:
252
- pass
253
- except:
254
- pass
255
 
256
- def process_cookies_file(cookies_file):
257
- """Process uploaded cookies file and return the path"""
258
- if cookies_file is None:
259
- return None
260
-
261
  try:
262
- # Create a temporary file for cookies
263
- temp_cookies_path = tempfile.mktemp(suffix='.txt')
264
-
265
- # Copy the uploaded file to temp location
266
- shutil.copy2(cookies_file.name, temp_cookies_path)
267
-
268
- return temp_cookies_path
 
269
  except Exception as e:
270
- print(f"Error processing cookies file: {e}")
271
- return None
272
 
273
- def process_video(url, cookies_file, progress=gr.Progress()):
274
- """Main function to process YouTube video"""
275
-
276
- # Check if required packages are available
277
- if not YT_DLP_AVAILABLE:
278
- return "Error: yt-dlp is not installed properly. Please install it using: pip install yt-dlp", "", "❌ Error: Missing yt-dlp"
279
-
280
- if not WHISPER_AVAILABLE:
281
- return "Error: OpenAI Whisper is not installed properly. Please install it using: pip install openai-whisper", "", "❌ Error: Missing Whisper"
282
-
283
- if not url or not url.strip():
284
- return "Please provide a valid YouTube URL", "", "❌ Error: Invalid URL"
285
-
286
- audio_path = None
287
- cookies_temp_path = None
288
-
289
  try:
290
- # Validate URL
291
- if not any(domain in url.lower() for domain in ['youtube.com', 'youtu.be']):
292
- return "Please provide a valid YouTube URL", "", "❌ Error: Invalid URL"
293
-
294
- # Process cookies file if provided
295
- progress(0.05, desc="Processing cookies...")
296
- cookies_temp_path = process_cookies_file(cookies_file)
297
-
298
- status_msg = "βœ… Cookies loaded" if cookies_temp_path else "⚠️ No cookies (may encounter bot detection)"
299
-
300
- # Download audio
301
- progress(0.2, desc="Downloading audio...")
302
- audio_path = download_audio(url, cookies_temp_path)
303
-
304
- # Transcribe audio
305
- progress(0.6, desc="Transcribing audio...")
306
- transcript = transcribe_audio(audio_path)
307
-
308
- if not transcript.strip():
309
- return "No speech detected in the video", "", "❌ No speech detected"
310
-
311
- # Extract stock information
312
- progress(0.9, desc="Extracting stock information...")
313
- stock_details = extract_stock_info_simple(transcript)
314
-
315
- progress(1.0, desc="Complete!")
316
- return transcript, stock_details, "βœ… Processing completed successfully"
317
-
318
  except Exception as e:
319
- error_msg = f"Error processing video: {str(e)}"
320
- return error_msg, "", f"❌ Error: {str(e)}"
321
-
322
- finally:
323
- # Clean up temporary files
324
- cleanup_file(audio_path)
325
- cleanup_file(cookies_temp_path)
326
-
327
- # Create Gradio interface
328
- with gr.Blocks(
329
- title="Stock Recommendation Extractor",
330
- theme=gr.themes.Soft(),
331
- css="""
332
- .gradio-container {
333
- max-width: 1400px;
334
- margin: auto;
335
- }
336
- .status-box {
337
- padding: 10px;
338
- border-radius: 5px;
339
- margin: 10px 0;
340
- }
341
- """
342
- ) as demo:
343
-
344
  gr.Markdown("""
345
- # πŸ“ˆ Stock Recommendation Extractor from YouTube
346
-
347
- Extract stock recommendations and trading information from YouTube videos using AI transcription.
348
-
349
- **How it works:**
350
- 1. Upload your cookies.txt file (optional but recommended to avoid bot detection)
351
- 2. Paste YouTube video URL
352
- 3. Downloads audio from YouTube video
353
- 4. Transcribes using OpenAI Whisper
354
- 5. Extracts stock-related information
355
-
356
- **⚠️ Disclaimer:** This is for educational purposes only. Always do your own research!
357
  """)
358
-
359
- with gr.Row():
360
- with gr.Column(scale=1):
361
- # Requirements check button
362
- gr.Markdown("### πŸ” System Check")
363
- check_req_btn = gr.Button(
364
- "Check Requirements",
365
- variant="secondary",
366
- size="sm"
367
- )
368
-
369
- requirements_output = gr.Textbox(
370
- label="πŸ“‹ Requirements Status",
371
- lines=10,
372
- interactive=False,
373
- visible=False
374
- )
375
-
376
- # Cookies file upload
377
- cookies_input = gr.File(
378
- label="πŸͺ Upload Cookies File (cookies.txt)",
379
- file_types=[".txt"],
380
- file_count="single"
381
- )
382
-
383
- gr.Markdown("""
384
- **How to get cookies.txt to fix 403 Forbidden errors:**
385
- 1. Install browser extension: "Get cookies.txt LOCALLY"
386
- 2. Visit YouTube in your browser (while logged in)
387
- 3. Click the extension icon and export cookies for youtube.com
388
- 4. Upload the downloaded cookies.txt file here
389
-
390
- **Alternative extensions:**
391
- - "cookies.txt" (Chrome/Firefox)
392
- - "Export Cookies" (Chrome)
393
-
394
- ⚠️ **Important**: Without cookies, you'll likely get 403 Forbidden errors
395
- """)
396
-
397
- url_input = gr.Textbox(
398
- label="πŸ“Ί YouTube URL",
399
- placeholder="https://www.youtube.com/watch?v=...",
400
- lines=2
401
- )
402
-
403
- process_btn = gr.Button(
404
- "πŸš€ Extract Stock Information",
405
- variant="primary",
406
- size="lg"
407
- )
408
-
409
- # Status display
410
- status_output = gr.Textbox(
411
- label="πŸ“Š Status",
412
- lines=1,
413
- interactive=False
414
- )
415
-
416
- gr.Markdown("""
417
- ### πŸ’‘ Tips:
418
- - **MUST upload cookies.txt** to avoid 403 Forbidden errors
419
- - Works best with financial YouTube channels
420
- - Ensure video has clear audio
421
- - English content works best
422
- - Try shorter videos first (under 10 minutes)
423
- """)
424
-
425
  with gr.Row():
426
- with gr.Column():
427
- transcript_output = gr.Textbox(
428
- label="πŸ“ Full Transcript",
429
- lines=15,
430
- max_lines=20,
431
- show_copy_button=True
432
- )
433
-
434
- with gr.Column():
435
- stock_info_output = gr.Textbox(
436
- label="πŸ“Š Extracted Stock Information",
437
- lines=15,
438
- max_lines=20,
439
- show_copy_button=True
440
- )
441
-
442
- # Event handlers
443
- def show_requirements():
444
- status = check_requirements()
445
- return gr.update(value=status, visible=True)
446
-
447
- check_req_btn.click(
448
- fn=show_requirements,
449
- outputs=[requirements_output]
450
- )
451
-
452
- process_btn.click(
453
- fn=process_video,
454
- inputs=[url_input, cookies_input],
455
- outputs=[transcript_output, stock_info_output, status_output],
456
- show_progress=True
457
- )
458
-
459
- # Example section
460
- gr.Markdown("### πŸ“‹ Example URLs (Replace with actual financial videos)")
461
- gr.Examples(
462
- examples=[
463
- ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"],
464
- ],
465
- inputs=[url_input],
466
- label="Click to try example"
467
- )
468
-
469
- gr.Markdown("""
470
- ### πŸ”§ Installation & Troubleshooting:
471
-
472
- **Step 1: Click "Check Requirements" button above to see what's missing**
473
-
474
- **If you get "Whisper Missing" error:**
475
- ```bash
476
- pip install openai-whisper
477
- ```
478
-
479
- **If you get "yt-dlp Missing" error:**
480
- ```bash
481
- pip install yt-dlp
482
- ```
483
-
484
- **Install all requirements at once:**
485
- ```bash
486
- pip install gradio==4.44.0 yt-dlp==2023.12.30 openai-whisper==20231117 torch==2.1.0 torchaudio==2.1.0 numpy==1.24.3 regex==2023.8.8
487
- ```
488
-
489
- **Alternative Whisper installation:**
490
- ```bash
491
- pip install transformers torch torchaudio
492
- ```
493
-
494
- **If using virtual environment:**
495
- ```bash
496
- # Create and activate virtual environment first
497
- python -m venv myenv
498
- # Windows: myenv\\Scripts\\activate
499
- # Mac/Linux: source myenv/bin/activate
500
- # Then install packages
501
- pip install -r requirements.txt
502
- ```
503
-
504
- **Other Issues:**
505
- - **Bot Detection Error**: Upload your cookies.txt file
506
- - **No Audio Found**: Check if video has audio track
507
- - **Transcription Failed**: Video might be too long or audio quality poor
508
- - **No Stock Info**: Video might not contain financial content
509
- """)
510
 
511
  if __name__ == "__main__":
512
- demo.launch()
 
1
+ # βœ… Combined YouTube Analyzer with Stock Info Extractor
2
+ # ⬇️ Based on your working app + whisper + stock extraction
3
+
4
+ import gradio as gr
5
  import os
6
  import tempfile
 
 
 
7
  import shutil
8
+ import re
9
+ import torch
10
+ import numpy as np
11
+ from yt_dlp import YoutubeDL
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
 
13
+ # Whisper setup
14
  WHISPER_AVAILABLE = False
15
  WHISPER_TYPE = None
 
16
  try:
17
  import whisper
18
  WHISPER_AVAILABLE = True
19
  WHISPER_TYPE = "openai-whisper"
20
+ except ImportError:
 
 
21
  try:
22
  from transformers import pipeline
23
  WHISPER_AVAILABLE = True
24
  WHISPER_TYPE = "transformers"
25
+ except ImportError:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  pass
27
 
28
+ # Stock Info Extraction
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
 
30
  def extract_stock_info_simple(text):
 
31
  try:
32
  stock_info = []
 
 
 
 
 
 
 
 
 
 
33
  companies = re.findall(r'\b[A-Z][a-z]+(?:\s+[A-Z][a-z]+)*(?:\s+(?:Inc|Corp|Company|Ltd)\.?)?', text)
34
  symbols = re.findall(r'\b[A-Z]{2,5}\b', text)
35
  prices = re.findall(r'\$\d+(?:\.\d{2})?', text)
36
+ actions = re.findall(r'\b(?:buy|sell|hold|bullish|bearish|target|stop loss)\b', text, re.IGNORECASE)
37
+
 
38
  result = "=== EXTRACTED STOCK INFORMATION ===\n\n"
39
+
40
  if companies:
41
+ result += f"\U0001F4CA Mentioned Companies: {', '.join(set(companies[:10]))}\n\n"
 
42
  if symbols:
43
+ result += f"\U0001F524 Potential Stock Symbols: {', '.join(set(symbols[:10]))}\n\n"
 
44
  if prices:
45
+ result += f"\U0001F4B0 Price Mentions: {', '.join(set(prices[:10]))}\n\n"
 
46
  if actions:
47
+ result += f"\U0001F4C8 Trading Actions: {', '.join(set(actions[:10]))}\n\n"
48
+
 
49
  recommendations = []
50
  sentences = text.split('.')
51
  for sentence in sentences:
52
+ if any(word in sentence.lower() for word in ['buy', 'sell', 'target']):
53
+ if any(sym in sentence for sym in symbols[:5]):
54
  recommendations.append(sentence.strip())
55
+
56
  if recommendations:
57
+ result += "\U0001F3AF Potential Recommendations:\n"
58
  for rec in recommendations[:5]:
59
  result += f"β€’ {rec}\n"
60
+
61
  if not any([companies, symbols, prices, actions]):
62
+ result += "⚠️ No clear stock recommendations found.\n"
63
+
 
 
 
 
64
  return result
65
+
66
  except Exception as e:
67
  return f"Error extracting stock info: {str(e)}"
68
 
69
+ # Whisper Transcription
 
 
 
 
 
 
 
 
 
 
 
70
 
71
+ def transcribe_audio(file_path):
72
+ if not WHISPER_AVAILABLE:
73
+ return "❌ Whisper not available", ""
 
 
74
  try:
75
+ if WHISPER_TYPE == "openai-whisper":
76
+ model = whisper.load_model("tiny")
77
+ result = model.transcribe(file_path)
78
+ return result["text"], "βœ… Transcription complete"
79
+ else:
80
+ pipe = pipeline("automatic-speech-recognition", model="openai/whisper-tiny")
81
+ result = pipe(file_path)
82
+ return result["text"], "βœ… Transcription complete"
83
  except Exception as e:
84
+ return "❌ Transcription failed", str(e)
 
85
 
86
+ # Audio Downloader using yt-dlp
87
+
88
+ def download_audio_youtube(url, cookies_file=None):
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  try:
90
+ temp_dir = tempfile.mkdtemp()
91
+ out_path = os.path.join(temp_dir, "audio")
92
+ ydl_opts = {
93
+ 'format': 'bestaudio[ext=m4a]/bestaudio/best',
94
+ 'outtmpl': out_path + '.%(ext)s',
95
+ 'quiet': True,
96
+ 'noplaylist': True,
97
+ 'cookiefile': cookies_file if cookies_file else None,
98
+ 'user_agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)',
99
+ 'force_ipv4': True,
100
+ }
101
+ with YoutubeDL(ydl_opts) as ydl:
102
+ ydl.download([url])
103
+ for ext in ['.m4a', '.mp3', '.webm']:
104
+ full_path = out_path + ext
105
+ if os.path.exists(full_path):
106
+ return full_path, "βœ… Audio downloaded"
107
+ return None, "❌ Audio file not found"
 
 
 
 
 
 
 
 
 
 
108
  except Exception as e:
109
+ return None, f"❌ Download error: {str(e)}"
110
+
111
+ # Gradio UI
112
+
113
+ def full_pipeline(url, cookies):
114
+ if not url:
115
+ return "❌ Enter a valid YouTube URL", "", ""
116
+ temp_cookie = cookies.name if cookies else None
117
+ audio_path, msg = download_audio_youtube(url, temp_cookie)
118
+ if not audio_path:
119
+ return msg, "", ""
120
+ transcript, tmsg = transcribe_audio(audio_path)
121
+ if "❌" in transcript:
122
+ return msg, transcript, tmsg
123
+ stock_data = extract_stock_info_simple(transcript)
124
+ return "βœ… Complete", transcript, stock_data
125
+
126
+ # Gradio App
127
+ with gr.Blocks(title="πŸ“ˆ Stock Info Extractor from YouTube") as demo:
 
 
 
 
 
 
128
  gr.Markdown("""
129
+ # πŸ“ˆ Extract Stock Mentions from YouTube
130
+ Upload a YouTube link + cookies.txt, and extract trading mentions using Whisper + AI
 
 
 
 
 
 
 
 
 
 
131
  """)
132
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
  with gr.Row():
134
+ url_input = gr.Textbox(label="YouTube URL")
135
+ cookies_input = gr.File(label="cookies.txt (optional)", file_types=[".txt"])
136
+
137
+ run_btn = gr.Button("πŸš€ Run Extraction")
138
+ status = gr.Textbox(label="Status")
139
+ transcript_box = gr.Textbox(label="Transcript", lines=10)
140
+ stock_box = gr.Textbox(label="Stock Info", lines=10)
141
+
142
+ run_btn.click(fn=full_pipeline, inputs=[url_input, cookies_input], outputs=[status, transcript_box, stock_box])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
143
 
144
  if __name__ == "__main__":
145
+ demo.launch(debug=True)