bep40 commited on
Commit
29cb912
·
verified ·
1 Parent(s): 17be8c7

feat: add _extract_24h_video_url to shorts_carousel.py

Browse files
Files changed (1) hide show
  1. shorts_carousel.py +25 -10
shorts_carousel.py CHANGED
@@ -1,4 +1,4 @@
1
- """Self-contained 24h Shorts Carousel - scrapes video-tin-tuc page for actual video articles."""
2
  import re
3
  import requests
4
  from bs4 import BeautifulSoup
@@ -12,7 +12,7 @@ BASE_24H = "https://www.24h.com.vn"
12
 
13
  def scrape_24h_news_shorts():
14
  """Scrape https://www.24h.com.vn/video/video-tin-tuc-cvd769.html
15
- Returns only ACTUAL article links (pattern: -c{N}a{N}.html), not category pages."""
16
  try:
17
  url = f"{BASE_24H}/video/video-tin-tuc-cvd769.html"
18
  r = requests.get(url, headers=HEADERS, timeout=15)
@@ -21,7 +21,6 @@ def scrape_24h_news_shorts():
21
  articles, seen = [], set()
22
  for a in soup.find_all("a", href=True):
23
  href = a.get("href", "")
24
- # Only match real article URLs: ends with -c{digits}a{digits}.html
25
  if not re.search(r'-c\d+a\d+\.html$', href):
26
  continue
27
  if not href.startswith("http"):
@@ -29,7 +28,6 @@ def scrape_24h_news_shorts():
29
  if href in seen:
30
  continue
31
  seen.add(href)
32
- # Get title
33
  title = a.get("title", "") or ""
34
  img = a.find("img")
35
  if img and not title:
@@ -38,13 +36,11 @@ def scrape_24h_news_shorts():
38
  title = a.get_text(strip=True)
39
  if not title or len(title) < 10:
40
  continue
41
- # Get image - priority: data-original > data-src > src
42
  img_src = None
43
  if img:
44
  img_src = img.get("data-original") or img.get("data-src") or img.get("src") or ""
45
  if "base64" in img_src or len(img_src) < 20:
46
  img_src = None
47
- # Try parent element for image if not found in link
48
  if not img_src:
49
  parent = a.parent
50
  if parent:
@@ -54,7 +50,7 @@ def scrape_24h_news_shorts():
54
  if "base64" in img_src or len(img_src) < 20:
55
  img_src = None
56
  if not img_src:
57
- continue # Skip items without thumbnail for visual carousel
58
  articles.append({
59
  "title": title, "link": href, "img": img_src,
60
  "source": "24h", "is_video": True
@@ -64,6 +60,26 @@ def scrape_24h_news_shorts():
64
  return []
65
 
66
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  # CSS for the 9:16 shorts slider
68
  SHORTS_CSS = """
69
  /* Shorts 9:16 Slider */
@@ -78,8 +94,7 @@ SHORTS_CSS = """
78
 
79
  def render_shorts_carousel(shorts, esc_fn, safe_url_fn, make_id_fn, slug_fn):
80
  """Render 9:16 vertical shorts carousel HTML.
81
- Click opens the article directly (bdpOpen) so user sees the actual video.
82
- """
83
  vids = [v for v in shorts if v.get("img")]
84
  if not vids:
85
  return ""
@@ -90,7 +105,7 @@ def render_shorts_carousel(shorts, esc_fn, safe_url_fn, make_id_fn, slug_fn):
90
  title = v.get("title", "")
91
  aid = make_id_fn(link)
92
  sl = slug_fn(title)
93
- # Use bdpOpen to open article view with video player
94
  cjs = "window.bdpOpen('" + esc_fn(link) + "','" + aid + "','" + sl + "')"
95
  items.append(
96
  '<div class="vslide-shorts-item" onclick="' + cjs + '">'
 
1
+ """Self-contained 24h Shorts Carousel + video extraction."""
2
  import re
3
  import requests
4
  from bs4 import BeautifulSoup
 
12
 
13
  def scrape_24h_news_shorts():
14
  """Scrape https://www.24h.com.vn/video/video-tin-tuc-cvd769.html
15
+ Returns only ACTUAL article links (pattern: -c{N}a{N}.html)."""
16
  try:
17
  url = f"{BASE_24H}/video/video-tin-tuc-cvd769.html"
18
  r = requests.get(url, headers=HEADERS, timeout=15)
 
21
  articles, seen = [], set()
22
  for a in soup.find_all("a", href=True):
23
  href = a.get("href", "")
 
24
  if not re.search(r'-c\d+a\d+\.html$', href):
25
  continue
26
  if not href.startswith("http"):
 
28
  if href in seen:
29
  continue
30
  seen.add(href)
 
31
  title = a.get("title", "") or ""
32
  img = a.find("img")
33
  if img and not title:
 
36
  title = a.get_text(strip=True)
37
  if not title or len(title) < 10:
38
  continue
 
39
  img_src = None
40
  if img:
41
  img_src = img.get("data-original") or img.get("data-src") or img.get("src") or ""
42
  if "base64" in img_src or len(img_src) < 20:
43
  img_src = None
 
44
  if not img_src:
45
  parent = a.parent
46
  if parent:
 
50
  if "base64" in img_src or len(img_src) < 20:
51
  img_src = None
52
  if not img_src:
53
+ continue
54
  articles.append({
55
  "title": title, "link": href, "img": img_src,
56
  "source": "24h", "is_video": True
 
60
  return []
61
 
62
 
63
+ def _extract_24h_video_url(article_url):
64
+ """Extract m3u8 video URL + poster from a 24h article page.
65
+ Returns dict {src, poster, vtype} or None."""
66
+ try:
67
+ r = requests.get(article_url, headers=HEADERS, timeout=10)
68
+ r.encoding = "utf-8"
69
+ m3u8s = re.findall(r'(https?://cdn\.24h\.com\.vn/[^\s"\'\\]+\.m3u8)', r.text)
70
+ full = [u for u in m3u8s if '_720p' not in u]
71
+ if not full:
72
+ full = m3u8s
73
+ if not full:
74
+ return None
75
+ soup = BeautifulSoup(r.text, "lxml")
76
+ og = soup.find("meta", property="og:image")
77
+ poster = og.get("content", "") if og else ""
78
+ return {"src": full[0], "poster": poster, "vtype": "hls"}
79
+ except:
80
+ return None
81
+
82
+
83
  # CSS for the 9:16 shorts slider
84
  SHORTS_CSS = """
85
  /* Shorts 9:16 Slider */
 
94
 
95
  def render_shorts_carousel(shorts, esc_fn, safe_url_fn, make_id_fn, slug_fn):
96
  """Render 9:16 vertical shorts carousel HTML.
97
+ Click opens the shorts article in TikTok fullscreen mode."""
 
98
  vids = [v for v in shorts if v.get("img")]
99
  if not vids:
100
  return ""
 
105
  title = v.get("title", "")
106
  aid = make_id_fn(link)
107
  sl = slug_fn(title)
108
+ # bdpOpen opens the article - which now renders as TikTok feed for 24h
109
  cjs = "window.bdpOpen('" + esc_fn(link) + "','" + aid + "','" + sl + "')"
110
  items.append(
111
  '<div class="vslide-shorts-item" onclick="' + cjs + '">'