DivYonko commited on
Commit ·
2a149f0
1
Parent(s): 0ed17b2
Fix video title fetch for live streams + fix subtitle encoding
Browse files
app.py
CHANGED
|
@@ -569,13 +569,34 @@ def extract_video_id(url_or_id):
|
|
| 569 |
|
| 570 |
|
| 571 |
def fetch_video_title(video_id):
|
|
|
|
|
|
|
|
|
|
|
|
|
| 572 |
try:
|
| 573 |
-
import urllib.request
|
| 574 |
url = f"https://www.youtube.com/oembed?url=https://www.youtube.com/watch?v={video_id}&format=json"
|
| 575 |
with urllib.request.urlopen(url, timeout=5) as resp:
|
| 576 |
-
|
|
|
|
|
|
|
| 577 |
except Exception:
|
| 578 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 579 |
|
| 580 |
|
| 581 |
def clean_topic(val):
|
|
@@ -1098,11 +1119,11 @@ for _si, _ss in enumerate(st.session_state.streams):
|
|
| 1098 |
_st = _ss.get("video_title") or _ss.get("video_id")
|
| 1099 |
_sk = _ss.get("redis_key", "")
|
| 1100 |
if _st and (store_llen(_sk) > 0 or is_scraper_running(_si)):
|
| 1101 |
-
_all_titles.append(f"
|
| 1102 |
if _all_titles:
|
| 1103 |
-
_subtitle = "
|
| 1104 |
else:
|
| 1105 |
-
_subtitle = "Real-time sentiment
|
| 1106 |
|
| 1107 |
# Build active stream pills for header
|
| 1108 |
_active_stream_pills = ""
|
|
|
|
| 569 |
|
| 570 |
|
| 571 |
def fetch_video_title(video_id):
|
| 572 |
+
"""Try oembed first (works for non-live), then YouTube Data API v3 (works for live)."""
|
| 573 |
+
import urllib.request
|
| 574 |
+
import urllib.parse
|
| 575 |
+
# Try oembed first (fast, no API key needed)
|
| 576 |
try:
|
|
|
|
| 577 |
url = f"https://www.youtube.com/oembed?url=https://www.youtube.com/watch?v={video_id}&format=json"
|
| 578 |
with urllib.request.urlopen(url, timeout=5) as resp:
|
| 579 |
+
title = json.loads(resp.read()).get("title")
|
| 580 |
+
if title:
|
| 581 |
+
return title
|
| 582 |
except Exception:
|
| 583 |
+
pass
|
| 584 |
+
# Fallback: YouTube Data API v3 (works for live streams)
|
| 585 |
+
try:
|
| 586 |
+
api_key = os.getenv("YOUTUBE_API_KEY", "")
|
| 587 |
+
if api_key:
|
| 588 |
+
url = (
|
| 589 |
+
"https://www.googleapis.com/youtube/v3/videos"
|
| 590 |
+
f"?part=snippet&id={urllib.parse.quote(video_id)}&key={api_key}"
|
| 591 |
+
)
|
| 592 |
+
with urllib.request.urlopen(url, timeout=5) as resp:
|
| 593 |
+
data = json.loads(resp.read())
|
| 594 |
+
items = data.get("items", [])
|
| 595 |
+
if items:
|
| 596 |
+
return items[0]["snippet"]["title"]
|
| 597 |
+
except Exception:
|
| 598 |
+
pass
|
| 599 |
+
return None
|
| 600 |
|
| 601 |
|
| 602 |
def clean_topic(val):
|
|
|
|
| 1119 |
_st = _ss.get("video_title") or _ss.get("video_id")
|
| 1120 |
_sk = _ss.get("redis_key", "")
|
| 1121 |
if _st and (store_llen(_sk) > 0 or is_scraper_running(_si)):
|
| 1122 |
+
_all_titles.append(f"\u25b6 {_st}")
|
| 1123 |
if _all_titles:
|
| 1124 |
+
_subtitle = " \u00b7 ".join(_all_titles)
|
| 1125 |
else:
|
| 1126 |
+
_subtitle = "Real-time sentiment \u00b7 topic classification \u00b7 engagement insights"
|
| 1127 |
|
| 1128 |
# Build active stream pills for header
|
| 1129 |
_active_stream_pills = ""
|
shared.py
CHANGED
|
@@ -332,13 +332,32 @@ def extract_video_id(url_or_id: str) -> str:
|
|
| 332 |
|
| 333 |
|
| 334 |
def fetch_video_title(video_id: str) -> str | None:
|
|
|
|
|
|
|
|
|
|
| 335 |
try:
|
| 336 |
-
import urllib.request
|
| 337 |
url = f"https://www.youtube.com/oembed?url=https://www.youtube.com/watch?v={video_id}&format=json"
|
| 338 |
with urllib.request.urlopen(url, timeout=5) as resp:
|
| 339 |
-
|
|
|
|
|
|
|
| 340 |
except Exception:
|
| 341 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 342 |
|
| 343 |
|
| 344 |
def clean_topic(val) -> str:
|
|
|
|
| 332 |
|
| 333 |
|
| 334 |
def fetch_video_title(video_id: str) -> str | None:
|
| 335 |
+
"""Try oembed first (works for non-live), then YouTube Data API v3 (works for live)."""
|
| 336 |
+
import urllib.request
|
| 337 |
+
import urllib.parse
|
| 338 |
try:
|
|
|
|
| 339 |
url = f"https://www.youtube.com/oembed?url=https://www.youtube.com/watch?v={video_id}&format=json"
|
| 340 |
with urllib.request.urlopen(url, timeout=5) as resp:
|
| 341 |
+
title = json.loads(resp.read()).get("title")
|
| 342 |
+
if title:
|
| 343 |
+
return title
|
| 344 |
except Exception:
|
| 345 |
+
pass
|
| 346 |
+
try:
|
| 347 |
+
api_key = os.getenv("YOUTUBE_API_KEY", "")
|
| 348 |
+
if api_key:
|
| 349 |
+
url = (
|
| 350 |
+
"https://www.googleapis.com/youtube/v3/videos"
|
| 351 |
+
f"?part=snippet&id={urllib.parse.quote(video_id)}&key={api_key}"
|
| 352 |
+
)
|
| 353 |
+
with urllib.request.urlopen(url, timeout=5) as resp:
|
| 354 |
+
data = json.loads(resp.read())
|
| 355 |
+
items = data.get("items", [])
|
| 356 |
+
if items:
|
| 357 |
+
return items[0]["snippet"]["title"]
|
| 358 |
+
except Exception:
|
| 359 |
+
pass
|
| 360 |
+
return None
|
| 361 |
|
| 362 |
|
| 363 |
def clean_topic(val) -> str:
|