Spaces:
Running
Running
fix: expand all_parts into separate slides in TikTok feed
Browse files- app_wrapper.py +21 -32
app_wrapper.py
CHANGED
|
@@ -53,12 +53,7 @@ _orig_render_video_slider = getattr(app, 'render_video_slider_html', None)
|
|
| 53 |
if _orig_render_video_slider:
|
| 54 |
def _patched_render_video_slider(videos):
|
| 55 |
html = _orig_render_video_slider(videos)
|
| 56 |
-
# Replace bdpOpenTikTok('url','aid') with bdpOpen('url','aid','')
|
| 57 |
-
# The original has: window.bdpOpenTikTok('url','aid')
|
| 58 |
-
# We need: window.bdpOpen('url','aid','')
|
| 59 |
html = html.replace("window.bdpOpenTikTok(", "window.bdpOpen(")
|
| 60 |
-
# Now fix: bdpOpen('url','aid') needs third param → add ,'')
|
| 61 |
-
# Pattern: bdpOpen('...','...') → bdpOpen('...','...','')
|
| 62 |
html = _re.sub(
|
| 63 |
r"window\.bdpOpen\('([^']+)','([^']+)'\)",
|
| 64 |
r"window.bdpOpen('\1','\2','')",
|
|
@@ -101,66 +96,51 @@ _orig_read_article = app.read_article
|
|
| 101 |
def _patched_read_article(url):
|
| 102 |
if not url or url == "#" or len(url) < 10:
|
| 103 |
return "<p>Không tìm thấy bài viết.</p>"
|
| 104 |
-
|
| 105 |
-
# 24h URLs → TikTok feed with all 24h highlight videos
|
| 106 |
if "24h.com.vn" in url:
|
| 107 |
result = _render_24h_tiktok_feed(url)
|
| 108 |
if result:
|
| 109 |
return result
|
| 110 |
-
|
| 111 |
-
# BDP video URLs → BDP TikTok feed
|
| 112 |
if "bongdaplus.vn/video/" in url:
|
| 113 |
result = _render_bdp_tiktok_feed(url)
|
| 114 |
if result:
|
| 115 |
return result
|
| 116 |
-
|
| 117 |
return _orig_read_article(url)
|
| 118 |
|
| 119 |
def _render_bdp_tiktok_feed(current_url):
|
| 120 |
-
"""Render BDP video in TikTok feed style."""
|
| 121 |
from concurrent.futures import ThreadPoolExecutor, as_completed
|
| 122 |
-
|
| 123 |
try:
|
| 124 |
bdp_list = app.scrape_bdp_video_list()[:15]
|
| 125 |
except:
|
| 126 |
return None
|
| 127 |
-
|
| 128 |
if not bdp_list:
|
| 129 |
return None
|
| 130 |
-
|
| 131 |
def _fetch_bdp(art):
|
| 132 |
vid_id = app._extract_bdp_video_id(art["link"])
|
| 133 |
if not vid_id: return None
|
| 134 |
embed = app.fetch_bdp_embed_data(vid_id)
|
| 135 |
if not embed or not embed.get("mp4"): return None
|
| 136 |
-
return {"title": art["title"], "link": art["link"], "img": art.get("img",""),
|
| 137 |
-
"src": embed["mp4"], "poster": embed.get("poster",""), "vtype": "mp4"}
|
| 138 |
-
|
| 139 |
videos = []
|
| 140 |
with ThreadPoolExecutor(max_workers=6) as ex:
|
| 141 |
futures = {ex.submit(_fetch_bdp, a): a for a in bdp_list}
|
| 142 |
for f in as_completed(futures):
|
| 143 |
try:
|
| 144 |
r = f.result()
|
| 145 |
-
if r: videos.
|
| 146 |
except: pass
|
| 147 |
-
|
| 148 |
if not videos:
|
| 149 |
return None
|
| 150 |
-
|
| 151 |
return _build_tiktok_html(videos, current_url, "BDP")
|
| 152 |
|
| 153 |
def _render_24h_tiktok_feed(current_url):
|
| 154 |
-
"""Render 24h video in TikTok feed
|
| 155 |
from concurrent.futures import ThreadPoolExecutor, as_completed
|
| 156 |
|
| 157 |
-
# Use highlight list (has more video content than shorts)
|
| 158 |
try:
|
| 159 |
all_articles = app.scrape_24h_video_list()[:20]
|
| 160 |
except:
|
| 161 |
all_articles = []
|
| 162 |
-
|
| 163 |
-
# Also add shorts if highlight list is short
|
| 164 |
if len(all_articles) < 10:
|
| 165 |
try:
|
| 166 |
shorts = scrape_24h_news_shorts()[:10]
|
|
@@ -169,16 +149,27 @@ def _render_24h_tiktok_feed(current_url):
|
|
| 169 |
if s["link"] not in seen:
|
| 170 |
all_articles.append(s)
|
| 171 |
except: pass
|
| 172 |
-
|
| 173 |
if not all_articles:
|
| 174 |
return None
|
| 175 |
|
| 176 |
def _fetch_video(art):
|
|
|
|
| 177 |
vid = _extract_24h_video_url(art["link"])
|
| 178 |
-
if vid:
|
| 179 |
-
return
|
| 180 |
-
|
| 181 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 182 |
|
| 183 |
videos = []
|
| 184 |
with ThreadPoolExecutor(max_workers=8) as ex:
|
|
@@ -186,12 +177,11 @@ def _render_24h_tiktok_feed(current_url):
|
|
| 186 |
for f in as_completed(futures):
|
| 187 |
try:
|
| 188 |
r = f.result()
|
| 189 |
-
if r: videos.
|
| 190 |
except: pass
|
| 191 |
|
| 192 |
if not videos:
|
| 193 |
return None
|
| 194 |
-
|
| 195 |
return _build_tiktok_html(videos, current_url, "24h")
|
| 196 |
|
| 197 |
def _build_tiktok_html(videos, current_url, source_label):
|
|
@@ -246,7 +236,6 @@ def _build_tiktok_html(videos, current_url, source_label):
|
|
| 246 |
<span class="tiktok-counter">{vi+1}/{len(ordered)}</span>
|
| 247 |
</div>''')
|
| 248 |
|
| 249 |
-
# Video list cards below
|
| 250 |
list_cards = []
|
| 251 |
for v in ordered:
|
| 252 |
img = app.safe_url(v.get("img") or v.get("poster",""))
|
|
|
|
| 53 |
if _orig_render_video_slider:
|
| 54 |
def _patched_render_video_slider(videos):
|
| 55 |
html = _orig_render_video_slider(videos)
|
|
|
|
|
|
|
|
|
|
| 56 |
html = html.replace("window.bdpOpenTikTok(", "window.bdpOpen(")
|
|
|
|
|
|
|
| 57 |
html = _re.sub(
|
| 58 |
r"window\.bdpOpen\('([^']+)','([^']+)'\)",
|
| 59 |
r"window.bdpOpen('\1','\2','')",
|
|
|
|
| 96 |
def _patched_read_article(url):
|
| 97 |
if not url or url == "#" or len(url) < 10:
|
| 98 |
return "<p>Không tìm thấy bài viết.</p>"
|
|
|
|
|
|
|
| 99 |
if "24h.com.vn" in url:
|
| 100 |
result = _render_24h_tiktok_feed(url)
|
| 101 |
if result:
|
| 102 |
return result
|
|
|
|
|
|
|
| 103 |
if "bongdaplus.vn/video/" in url:
|
| 104 |
result = _render_bdp_tiktok_feed(url)
|
| 105 |
if result:
|
| 106 |
return result
|
|
|
|
| 107 |
return _orig_read_article(url)
|
| 108 |
|
| 109 |
def _render_bdp_tiktok_feed(current_url):
|
|
|
|
| 110 |
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
|
|
| 111 |
try:
|
| 112 |
bdp_list = app.scrape_bdp_video_list()[:15]
|
| 113 |
except:
|
| 114 |
return None
|
|
|
|
| 115 |
if not bdp_list:
|
| 116 |
return None
|
|
|
|
| 117 |
def _fetch_bdp(art):
|
| 118 |
vid_id = app._extract_bdp_video_id(art["link"])
|
| 119 |
if not vid_id: return None
|
| 120 |
embed = app.fetch_bdp_embed_data(vid_id)
|
| 121 |
if not embed or not embed.get("mp4"): return None
|
| 122 |
+
return [{"title": art["title"], "link": art["link"], "img": art.get("img",""),
|
| 123 |
+
"src": embed["mp4"], "poster": embed.get("poster",""), "vtype": "mp4"}]
|
|
|
|
| 124 |
videos = []
|
| 125 |
with ThreadPoolExecutor(max_workers=6) as ex:
|
| 126 |
futures = {ex.submit(_fetch_bdp, a): a for a in bdp_list}
|
| 127 |
for f in as_completed(futures):
|
| 128 |
try:
|
| 129 |
r = f.result()
|
| 130 |
+
if r: videos.extend(r)
|
| 131 |
except: pass
|
|
|
|
| 132 |
if not videos:
|
| 133 |
return None
|
|
|
|
| 134 |
return _build_tiktok_html(videos, current_url, "BDP")
|
| 135 |
|
| 136 |
def _render_24h_tiktok_feed(current_url):
|
| 137 |
+
"""Render 24h video in TikTok feed. Expands multi-part videos into separate slides."""
|
| 138 |
from concurrent.futures import ThreadPoolExecutor, as_completed
|
| 139 |
|
|
|
|
| 140 |
try:
|
| 141 |
all_articles = app.scrape_24h_video_list()[:20]
|
| 142 |
except:
|
| 143 |
all_articles = []
|
|
|
|
|
|
|
| 144 |
if len(all_articles) < 10:
|
| 145 |
try:
|
| 146 |
shorts = scrape_24h_news_shorts()[:10]
|
|
|
|
| 149 |
if s["link"] not in seen:
|
| 150 |
all_articles.append(s)
|
| 151 |
except: pass
|
|
|
|
| 152 |
if not all_articles:
|
| 153 |
return None
|
| 154 |
|
| 155 |
def _fetch_video(art):
|
| 156 |
+
"""Fetch video - returns LIST of dicts (multiple parts if found)."""
|
| 157 |
vid = _extract_24h_video_url(art["link"])
|
| 158 |
+
if not vid:
|
| 159 |
+
return []
|
| 160 |
+
results = []
|
| 161 |
+
all_parts = vid.get("all_parts", [vid["src"]])
|
| 162 |
+
for pi, part_url in enumerate(all_parts):
|
| 163 |
+
label = f" (Phần {pi+1})" if len(all_parts) > 1 else ""
|
| 164 |
+
results.append({
|
| 165 |
+
"title": art["title"] + label,
|
| 166 |
+
"link": art["link"],
|
| 167 |
+
"img": art.get("img", ""),
|
| 168 |
+
"src": part_url,
|
| 169 |
+
"poster": vid["poster"],
|
| 170 |
+
"vtype": vid["vtype"]
|
| 171 |
+
})
|
| 172 |
+
return results
|
| 173 |
|
| 174 |
videos = []
|
| 175 |
with ThreadPoolExecutor(max_workers=8) as ex:
|
|
|
|
| 177 |
for f in as_completed(futures):
|
| 178 |
try:
|
| 179 |
r = f.result()
|
| 180 |
+
if r: videos.extend(r)
|
| 181 |
except: pass
|
| 182 |
|
| 183 |
if not videos:
|
| 184 |
return None
|
|
|
|
| 185 |
return _build_tiktok_html(videos, current_url, "24h")
|
| 186 |
|
| 187 |
def _build_tiktok_html(videos, current_url, source_label):
|
|
|
|
| 236 |
<span class="tiktok-counter">{vi+1}/{len(ordered)}</span>
|
| 237 |
</div>''')
|
| 238 |
|
|
|
|
| 239 |
list_cards = []
|
| 240 |
for v in ordered:
|
| 241 |
img = app.safe_url(v.get("img") or v.get("poster",""))
|