areebsatin commited on
Commit
2323e40
Β·
1 Parent(s): 5680194

Feature: YouTube heatmap extraction for viral segment detection

Browse files
Files changed (1) hide show
  1. server.py +80 -1
server.py CHANGED
@@ -405,7 +405,6 @@ def download_video(youtube_url: str, max_retries: int = 3) -> Path:
405
  raise RuntimeError(f"Download failed after {max_retries} attempts:\n{last_err}")
406
 
407
 
408
- # ── Step 2: Video info ────────────────────────────────────────────────────────
409
  def get_video_info(video_path: Path):
410
  """Return (duration, width, height) via ffprobe JSON."""
411
  r = subprocess.run(
@@ -424,6 +423,83 @@ def get_video_info(video_path: Path):
424
  return duration, width, height
425
 
426
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
427
  # ── Step 3: Audio energy analysis ────────────────────────────────────────────
428
  def extract_audio_energy(video_path: Path, duration: float):
429
  """
@@ -921,6 +997,9 @@ def process():
921
  try:
922
  # 1. Download
923
  video_path = download_video(youtube_url)
 
 
 
924
 
925
  # 2. Info
926
  duration, width, height = get_video_info(video_path)
 
405
  raise RuntimeError(f"Download failed after {max_retries} attempts:\n{last_err}")
406
 
407
 
 
408
  def get_video_info(video_path: Path):
409
  """Return (duration, width, height) via ffprobe JSON."""
410
  r = subprocess.run(
 
423
  return duration, width, height
424
 
425
 
426
+ def get_youtube_heatmap(video_path: Path, url: str):
427
+ """
428
+ Fetch YouTube's 'Most Replayed' heatmap data using yt-dlp.
429
+ Returns: [{start_time: float, end_time: float, score: float}] or []
430
+ """
431
+ print("[>>] Fetching YouTube heatmap...", flush=True)
432
+
433
+ # We use the same hardened bypass settings as download_video
434
+ info_json_path = video_path.with_suffix(".info.json")
435
+
436
+ cmd = [
437
+ "yt-dlp",
438
+ "--force-ipv4",
439
+ "--ignore-config",
440
+ "--no-cache-dir",
441
+ "--user-agent", _USER_AGENT,
442
+ "--extractor-args", "youtube:player_client=web,tv,ios;player_skip=web_embedded_check",
443
+ "--remote-components", "ejs:github",
444
+ "--no-check-certificates",
445
+ "--geo-bypass",
446
+ "--add-header", "Accept-Language:en-US,en;q=0.9",
447
+ "--add-header", "Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
448
+ "--write-info-json",
449
+ "--skip-download",
450
+ "-o", str(video_path.with_suffix("")), # This will result in filename.info.json
451
+ url,
452
+ ]
453
+
454
+ # Add cookies if available
455
+ cookies_path = _find_cookies_file()
456
+ if cookies_path:
457
+ cmd[1:1] = ["--cookies", cookies_path]
458
+
459
+ # Add Proxy if defined
460
+ if YTDLP_PROXY:
461
+ cmd[1:1] = ["--proxy", YTDLP_PROXY]
462
+
463
+ try:
464
+ # Run yt-dlp to get JSON
465
+ subprocess.run(cmd, capture_output=True, text=True, timeout=30)
466
+
467
+ if not info_json_path.exists():
468
+ print("[!] Heatmap info JSON not found.", flush=True)
469
+ return []
470
+
471
+ with open(info_json_path, "r", encoding="utf-8") as f:
472
+ data = json.load(f)
473
+
474
+ heatmap = data.get("heatmap")
475
+ if not heatmap:
476
+ print("[!] No heatmap data found in YouTube metadata.", flush=True)
477
+ return []
478
+
479
+ # Normalize scores to 0.0 - 1.0
480
+ max_val = max((item.get("value", 0) for item in heatmap), default=1.0)
481
+ if max_val == 0: max_val = 1.0
482
+
483
+ normalized = []
484
+ for item in heatmap:
485
+ normalized.append({
486
+ "start_time": float(item["start_time"]),
487
+ "end_time": float(item["end_time"]),
488
+ "score": round(float(item["value"]) / max_val, 4)
489
+ })
490
+
491
+ print(f"[OK] Extracted {len(normalized)} heatmap segments.", flush=True)
492
+
493
+ # Cleanup
494
+ try: info_json_path.unlink()
495
+ except: pass
496
+
497
+ return normalized
498
+ except Exception as e:
499
+ print(f"[!] Heatmap extraction error: {e}", flush=True)
500
+ return []
501
+
502
+
503
  # ── Step 3: Audio energy analysis ────────────────────────────────────────────
504
  def extract_audio_energy(video_path: Path, duration: float):
505
  """
 
997
  try:
998
  # 1. Download
999
  video_path = download_video(youtube_url)
1000
+
1001
+ # 1.1 Heatmap (New Viral Signalling)
1002
+ heatmap_data = get_youtube_heatmap(video_path, youtube_url)
1003
 
1004
  # 2. Info
1005
  duration, width, height = get_video_info(video_path)