VAIVI / startup.py
bep40's picture
fix: proper startup.py with 9:16 shorts carousel patching
23fb147 verified
"""Startup: patches app.py to add 24h news shorts (9:16 vertical) carousel, then runs it."""
import os, sys
APP_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "app.py")
def patch():
with open(APP_PATH, "r", encoding="utf-8") as f:
code = f.read()
if "vslide-shorts-item" in code:
return # Already fully patched
# 1. Add import
if "from shorts_scraper import scrape_24h_news_shorts" not in code:
code = code.replace(
"from concurrent.futures import ThreadPoolExecutor, as_completed",
"from concurrent.futures import ThreadPoolExecutor, as_completed\nfrom shorts_scraper import scrape_24h_news_shorts",
1
)
# 2. Add shorts fetch to fetch_homepage
if "h24_shorts" not in code:
code = code.replace(" h24_videos=[]", " h24_videos=[]\n h24_shorts=[]", 1)
# Add _fetch_shorts function
old_fetch24h = " def _fetch_24h():\n nonlocal h24_videos\n try: h24_videos=scrape_24h_video_list()[:15]\n except: pass"
new_fetch24h = old_fetch24h + "\n def _fetch_shorts():\n nonlocal h24_shorts\n try: h24_shorts=scrape_24h_news_shorts()[:15]\n except: pass"
code = code.replace(old_fetch24h, new_fetch24h, 1)
# Submit _fetch_shorts
code = code.replace(" ex.submit(_fetch_24h)\n futures=", " ex.submit(_fetch_24h)\n ex.submit(_fetch_shorts)\n futures=", 1)
# Return shorts
code = code.replace(" return all_articles, h24_videos", " return all_articles, h24_videos, h24_shorts", 1)
# 3. Update fetch_news_list unpacking
if "h24_shorts = fetch_homepage" not in code:
code = code.replace(
" articles, h24_videos = fetch_homepage()\n return render_homepage_html(articles, h24_videos)",
" articles, h24_videos, h24_shorts = fetch_homepage()\n return render_homepage_html(articles, h24_videos, h24_shorts)",
1
)
# 4. Add render_shorts_carousel_html
if "render_shorts_carousel_html" not in code:
fn = [
'def render_shorts_carousel_html(shorts):',
' """9:16 vertical shorts carousel from 24h video-tin-tuc."""',
' vids = [v for v in shorts if v.get("img")]',
' if not vids: return ""',
' items = []',
' for v in vids[:15]:',
' img = safe_url(v.get("img",""))',
' link = v.get("link","#")',
' title = v.get("title","")',
' aid = make_id(link)',
" cjs = \"window.bdpOpenTikTok('\"+esc(link)+\"','\"+aid+\"')\"",
' items.append(\'<div class="vslide-shorts-item" onclick="\'+cjs+\'">\' +',
' \'<div class="vslide-shorts-thumb"><img src="\'+img+\'" alt="" class="bdp-lazy-img">\' +',
' \'<div class="vslide-play">\\u25b6</div></div>\' +',
' \'<p class="vslide-shorts-title">\'+title+\'</p></div>\')',
' h = \'<div class="vslide-wrap"><div class="vslide-header">\'',
' h += \'<span class="vslide-label">\\U0001f4f1 Shorts Tin T\\u1ee9c 24h</span>\'',
" h += '<div class=\"vslide-nav\"><button class=\"vslide-btn\" onclick=\"window.bdpSlideScroll(-1,\\'vslide-shorts\\')\">\\u25c0</button>'",
" h += '<button class=\"vslide-btn\" onclick=\"window.bdpSlideScroll(1,\\'vslide-shorts\\')\">\\u25b6</button></div></div>'",
' h += \'<div class="vslide-track" id="vslide-shorts">\'+\'\'.join(items)+\'</div></div>\'',
' return h',
'',
]
fn_code = '\n'.join(fn) + '\n'
code = code.replace(
"def render_video_carousel_html(videos):",
fn_code + "def render_video_carousel_html(videos):",
1
)
# 5. Update render_homepage_html
if "h24_shorts=None" not in code:
code = code.replace(
"def render_homepage_html(articles, h24_videos=None):",
"def render_homepage_html(articles, h24_videos=None, h24_shorts=None):",
1
)
if "shorts_carousel" not in code:
code = code.replace(
" video_carousel = render_video_carousel_html(h24_videos or [])",
" shorts_carousel = render_shorts_carousel_html(h24_shorts or [])\n video_carousel = render_video_carousel_html(h24_videos or [])",
1
)
code = code.replace(
"{video_carousel}{news_carousel}",
"{shorts_carousel}{video_carousel}{news_carousel}",
1
)
# 6. Add CSS for 9:16 shorts
if "vslide-shorts-item" not in code:
shorts_css = (
'\n/* Shorts 9:16 Slider */\n'
'.vslide-shorts-item{flex:0 0 110px;scroll-snap-align:start;cursor:pointer;transition:transform .15s}\n'
'.vslide-shorts-item:hover{transform:scale(1.04)}\n'
'@media(min-width:768px){.vslide-shorts-item{flex:0 0 130px}}\n'
'.vslide-shorts-thumb{position:relative;width:100%;aspect-ratio:9/16;border-radius:12px;overflow:hidden;background:#222}\n'
'.vslide-shorts-thumb img{width:100%;height:100%;object-fit:cover}\n'
'.vslide-shorts-title{color:#ccc;font-size:10.5px;margin:5px 0 0;line-height:1.3;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}\n'
)
code = code.replace(
".vslide-badge-24h{background:#e67e22;color:#fff}",
".vslide-badge-24h{background:#e67e22;color:#fff}" + shorts_css,
1
)
with open(APP_PATH, "w", encoding="utf-8") as f:
f.write(code)
print("[startup.py] Patched app.py with 9:16 shorts carousel")
# Run patch then execute app
patch()
# Execute patched app.py
with open(APP_PATH, encoding="utf-8") as f:
exec(compile(f.read(), APP_PATH, "exec"), {"__name__": "__main__", "__file__": APP_PATH})