Sasha commited on
Commit
c48eed2
·
1 Parent(s): a721446

feat: integrate automatic VOD ID resolution in chat_mod_worker.py

Browse files
Files changed (1) hide show
  1. local_worker/chat_mod_worker.py +117 -1
local_worker/chat_mod_worker.py CHANGED
@@ -350,9 +350,123 @@ def mod_action_sender():
350
  time.sleep(1)
351
 
352
  # =========================================================================
353
- # VOD CHAT BACKFILLING LOOP (NO AUDIO)
354
  # =========================================================================
355
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
356
  def load_processed_streams():
357
  """Load successfully processed stream IDs from a local file"""
358
  if not os.path.exists(PROCESSED_FILE):
@@ -376,6 +490,8 @@ def run_backfill_loop():
376
  """Main loop to check and backfill VOD chat comments ONLY"""
377
 
378
  while not stop_flag.is_set():
 
 
379
  print(f"[Backfill] Checking for pending VOD backfill tasks...")
380
  processed_streams = load_processed_streams()
381
 
 
350
  time.sleep(1)
351
 
352
  # =========================================================================
353
+ # VOD ID AUTO-RESOLUTION & CHAT BACKFILLING LOOP (NO AUDIO)
354
  # =========================================================================
355
 
356
+ def get_recent_twitch_vods(channel_name):
357
+ """Fetch recent Twitch VODs of a user using public Twitch GQL API"""
358
+ url = "https://gql.twitch.tv/gql"
359
+ headers = {
360
+ "Client-Id": "kimne78kx3ncx6brgo4mv6wki5h1ko",
361
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
362
+ }
363
+ payload = {
364
+ "query": """
365
+ query($login: String!) {
366
+ user(login: $login) {
367
+ videos(first: 10, type: ARCHIVE) {
368
+ edges {
369
+ node {
370
+ id
371
+ title
372
+ createdAt
373
+ lengthSeconds
374
+ }
375
+ }
376
+ }
377
+ }
378
+ }
379
+ """,
380
+ "variables": {
381
+ "login": channel_name
382
+ }
383
+ }
384
+ try:
385
+ res = requests.post(url, json=payload, headers=headers, timeout=10)
386
+ if res.status_code == 200:
387
+ data = res.json()
388
+ edges = data.get("data", {}).get("user", {}).get("videos", {}).get("edges", [])
389
+ return [edge.get("node") for edge in edges if edge and edge.get("node")]
390
+ except Exception as e:
391
+ print(f"[VOD Resolve] Error fetching Twitch VODs: {e}")
392
+ return []
393
+
394
+ def find_matching_vod(stream_start_time_iso, recent_vods):
395
+ try:
396
+ from datetime import datetime
397
+ clean_stream = stream_start_time_iso.replace('Z', '+00:00')
398
+ stream_dt = datetime.fromisoformat(clean_stream)
399
+ except Exception as e:
400
+ print(f"[Match] Error parsing stream start time: {e}")
401
+ return None
402
+
403
+ best_match = None
404
+ min_diff = float('inf')
405
+
406
+ for node in recent_vods:
407
+ vod_id = node.get("id")
408
+ created_at_raw = node.get("createdAt")
409
+ if not vod_id or not created_at_raw:
410
+ continue
411
+ try:
412
+ clean_vod = created_at_raw.replace('Z', '+00:00')
413
+ vod_dt = datetime.fromisoformat(clean_vod)
414
+ except Exception:
415
+ continue
416
+
417
+ diff = abs((stream_dt - vod_dt).total_seconds())
418
+ # If the start times are within 2.5 hours (9000 seconds)
419
+ if diff < 9000 and diff < min_diff:
420
+ min_diff = diff
421
+ best_match = vod_id
422
+
423
+ return best_match
424
+
425
+ def resolve_missing_vods():
426
+ """Find and resolve twitch_vod_id for pending streams that are missing it"""
427
+ headers = {"x-api-key": API_KEY}
428
+ try:
429
+ # 1. Fetch missing VOD streams from backend
430
+ res = requests.get(f"{API_URL}/api/streams/missing-vod", headers=headers, timeout=10)
431
+ if res.status_code != 200:
432
+ return
433
+
434
+ streams = res.json().get("streams", [])
435
+ if not streams:
436
+ return
437
+
438
+ print(f"[VOD Resolve] Found {len(streams)} pending stream(s) lacking VOD ID.")
439
+
440
+ # 2. Get recent VODs from Twitch
441
+ recent_vods = get_recent_twitch_vods(TWITCH_CHANNEL)
442
+ if not recent_vods:
443
+ print("[VOD Resolve] Could not retrieve recent Twitch VODs. Skipping resolve.")
444
+ return
445
+
446
+ # 3. Match each stream to a Twitch VOD
447
+ for stream in streams:
448
+ stream_id = stream.get("id")
449
+ start_time_str = stream.get("start_time")
450
+ if not stream_id or not start_time_str:
451
+ continue
452
+
453
+ matched_vod_id = find_matching_vod(start_time_str, recent_vods)
454
+ if matched_vod_id:
455
+ print(f"[VOD Resolve] Stream ID {stream_id} ({start_time_str}) matched with Twitch VOD {matched_vod_id}.")
456
+ # Send update to server
457
+ up_res = requests.post(f"{API_URL}/api/streams/{stream_id}/resolve-vod",
458
+ json={"twitchVodId": matched_vod_id},
459
+ headers={"x-api-key": API_KEY, "Content-Type": "application/json"},
460
+ timeout=10)
461
+ if up_res.status_code == 200:
462
+ print(f"[VOD Resolve] Successfully updated VOD ID for stream {stream_id}!")
463
+ else:
464
+ print(f"[VOD Resolve] Failed to update VOD ID: {up_res.status_code}")
465
+ else:
466
+ print(f"[VOD Resolve] No matching VOD found for stream ID {stream_id} ({start_time_str}) within timeframe.")
467
+ except Exception as e:
468
+ print(f"[VOD Resolve] Exception during resolve loop: {e}")
469
+
470
  def load_processed_streams():
471
  """Load successfully processed stream IDs from a local file"""
472
  if not os.path.exists(PROCESSED_FILE):
 
490
  """Main loop to check and backfill VOD chat comments ONLY"""
491
 
492
  while not stop_flag.is_set():
493
+ # Try to resolve any missing VOD IDs before checking pending backfills
494
+ resolve_missing_vods()
495
  print(f"[Backfill] Checking for pending VOD backfill tasks...")
496
  processed_streams = load_processed_streams()
497