Spaces:
Running
Running
fix: simpler JS scroll for video cards - no escape issues
Browse files- app_wrapper.py +7 -15
app_wrapper.py
CHANGED
|
@@ -4,16 +4,13 @@ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
| 4 |
from shorts_carousel import scrape_24h_news_shorts, render_shorts_carousel, SHORTS_CSS, _extract_24h_video_url
|
| 5 |
import app
|
| 6 |
|
| 7 |
-
# ββ Disable auto-refresh timer ββ
|
| 8 |
app.REFRESH_SECONDS = 9999
|
| 9 |
|
| 10 |
-
# ββ CSS ββ
|
| 11 |
SHORTS_EXTRA_CSS = """
|
| 12 |
.shorts-feed-mode .tiktok-video{object-fit:cover!important}
|
| 13 |
.tiktok-fullscreen-container{width:100%!important;max-width:100%!important;border-radius:0!important;height:80vh!important;max-height:680px!important;min-height:400px!important}
|
| 14 |
.tiktok-fullscreen-feed{height:100%!important}
|
| 15 |
.tiktok-slide{height:80vh!important;max-height:680px!important;min-height:400px!important}
|
| 16 |
-
/* Highlight video default 1:1 (square crop center) */
|
| 17 |
.tiktok-fullscreen-container:not(.shorts-feed-mode) .tiktok-video{object-fit:cover!important}
|
| 18 |
.tiktok-fullscreen-container.contain-mode .tiktok-video{object-fit:contain!important}
|
| 19 |
.tiktok-fullheight-btn{position:absolute;bottom:70px;right:12px;background:rgba(255,255,255,.15);color:#fff;border:none;padding:6px 12px;border-radius:16px;font-size:11px;cursor:pointer;z-index:8;backdrop-filter:blur(6px)}
|
|
@@ -33,7 +30,6 @@ if "vslide-shorts-item" not in app.CSS: app.CSS += SHORTS_CSS
|
|
| 33 |
if "shorts-feed-mode" not in app.CSS: app.CSS += SHORTS_EXTRA_CSS
|
| 34 |
if "FULLWIDTH" not in app.CSS: app.CSS += FULLWIDTH_CSS
|
| 35 |
|
| 36 |
-
# ββ Patches ββ
|
| 37 |
def _fixed_extract(url):
|
| 38 |
vid=_extract_24h_video_url(url)
|
| 39 |
if not vid:return []
|
|
@@ -63,7 +59,6 @@ app.render_homepage_html=_php
|
|
| 63 |
_orig_vp=app.render_video_page_html
|
| 64 |
def _pvp():
|
| 65 |
html=_orig_vp()
|
| 66 |
-
# Add contain toggle button (default is 1:1 cover, button switches to contain)
|
| 67 |
btn='<button class="tiktok-fullheight-btn" onclick="event.stopPropagation();var c=this.closest(\'.tiktok-fullscreen-container\');c.classList.toggle(\'contain-mode\');var on=c.classList.contains(\'contain-mode\');c.querySelectorAll(\'.tiktok-fullheight-btn\').forEach(function(b){b.classList.toggle(\'active\',on);b.textContent=on?\'\\u2922 1:1\':\'\\u2B07 Contain\';});">\u2B07 Contain</button>'
|
| 68 |
return html.replace('<div class="tiktok-unmute-hint"',btn+'<div class="tiktok-unmute-hint"')
|
| 69 |
app.render_video_page_html=_pvp
|
|
@@ -84,9 +79,7 @@ def _pra(url):
|
|
| 84 |
return [{"title":a["title"]+(f" (P{i+1})" if len(ps)>1 else ""),"link":a["link"],"img":a.get("img",""),"src":p,"poster":v["poster"],"vtype":"hls"} for i,p in enumerate(ps)]
|
| 85 |
with ThreadPoolExecutor(8) as ex:
|
| 86 |
results=list(ex.map(_f,arts))
|
| 87 |
-
vids=[]
|
| 88 |
-
for r in results:
|
| 89 |
-
if r:vids.extend(r)
|
| 90 |
if vids:
|
| 91 |
ca=app.make_id(url);ordered=[v for v in vids if app.make_id(v["link"])==ca]+[v for v in vids if app.make_id(v["link"])!=ca]
|
| 92 |
return _btk(ordered,"24h")
|
|
@@ -103,9 +96,7 @@ def _pra(url):
|
|
| 103 |
return [{"title":a["title"],"link":a["link"],"img":a.get("img",""),"src":e["mp4"],"poster":e.get("poster",""),"vtype":"mp4"}]
|
| 104 |
with ThreadPoolExecutor(6) as ex:
|
| 105 |
results=list(ex.map(_fb,bl))
|
| 106 |
-
vids=[]
|
| 107 |
-
for r in results:
|
| 108 |
-
if r:vids.extend(r)
|
| 109 |
if vids:
|
| 110 |
ca=app.make_id(url);ordered=[v for v in vids if app.make_id(v["link"])==ca]+[v for v in vids if app.make_id(v["link"])!=ca]
|
| 111 |
return _btk(ordered,"BDP")
|
|
@@ -113,7 +104,7 @@ def _pra(url):
|
|
| 113 |
|
| 114 |
def _btk(ordered,sl):
|
| 115 |
badge="bdp-badge-24h" if sl=="24h" else "bdp-badge-bdp"
|
| 116 |
-
is_shorts
|
| 117 |
slides=[];cards=[]
|
| 118 |
for vi,v in enumerate(ordered):
|
| 119 |
po=app.safe_url(v.get("poster",""));pa=f' poster="{po}"' if po else ""
|
|
@@ -121,12 +112,13 @@ def _btk(ordered,sl):
|
|
| 121 |
orig_url=app.esc(v["link"])
|
| 122 |
sj=f"event.stopPropagation();window.bdpShareOriginal('{app.esc(v['title'])}','{orig_url}')"
|
| 123 |
vt=f'<video class="tiktok-video" playsinline preload="metadata"{pa} data-hls-src="{vs}" muted loop></video>' if v.get("vtype")=="hls" else f'<video class="tiktok-video" playsinline preload="metadata"{pa} muted loop><source src="{app.safe_url(vs)}" type="video/mp4"></video>'
|
| 124 |
-
# Contain toggle (default = 1:1 cover crop)
|
| 125 |
cb='<button class="tiktok-fullheight-btn" onclick="event.stopPropagation();var c=this.closest(\'.tiktok-fullscreen-container\');c.classList.toggle(\'contain-mode\');var on=c.classList.contains(\'contain-mode\');c.querySelectorAll(\'.tiktok-fullheight-btn\').forEach(function(b){b.classList.toggle(\'active\',on);b.textContent=on?\'\\u2922 1:1\':\'\\u2B07 Contain\';});">\u2B07 Contain</button>'
|
| 126 |
slides.append(f'<div class="tiktok-slide" data-index="{vi}" data-aid="{aid}">{vt}<div class="tiktok-pause-icon">\u25b6</div><div class="tiktok-seek-controls"><button class="tiktok-seek-btn" onclick="event.stopPropagation();window.bdpSeek(this,-10)">\u23ea 10s</button><button class="tiktok-seek-btn" onclick="event.stopPropagation();window.bdpSeek(this,10)">10s \u23e9</button></div><div class="tiktok-bottom"><span class="bdp-badge {badge}">{sl}</span><p class="tiktok-title">{v["title"]}</p><div class="tiktok-actions"><button class="tiktok-action-btn" onclick="{sj}">\U0001f4e4 Chia s\u1ebb</button></div></div>{cb}<div class="tiktok-unmute-hint" onclick="window.bdpTikTokUnmute(this)">\U0001f507 B\u1eadt ti\u1ebfng</div><span class="tiktok-counter">{vi+1}/{len(ordered)}</span></div>')
|
| 127 |
img=app.safe_url(v.get("img")or v.get("poster",""))
|
| 128 |
-
#
|
| 129 |
-
cards.append(f'<div class="bdp-card" onclick="
|
|
|
|
|
|
|
| 130 |
grid=f'<div class="bdp-wrap"><div class="bdp-topbar"><span>\U0001f3ac Video</span><span>{len(ordered)} video</span></div><div class="bdp-grid">{"".join(cards)}</div></div>'
|
| 131 |
mode_class="shorts-feed-mode" if is_shorts else ""
|
| 132 |
return f'<div class="tiktok-fullscreen-container {mode_class}" id="tiktok-fullscreen-feed"><div class="tiktok-fullscreen-feed">{"".join(slides)}</div></div>{grid}'
|
|
|
|
| 4 |
from shorts_carousel import scrape_24h_news_shorts, render_shorts_carousel, SHORTS_CSS, _extract_24h_video_url
|
| 5 |
import app
|
| 6 |
|
|
|
|
| 7 |
app.REFRESH_SECONDS = 9999
|
| 8 |
|
|
|
|
| 9 |
SHORTS_EXTRA_CSS = """
|
| 10 |
.shorts-feed-mode .tiktok-video{object-fit:cover!important}
|
| 11 |
.tiktok-fullscreen-container{width:100%!important;max-width:100%!important;border-radius:0!important;height:80vh!important;max-height:680px!important;min-height:400px!important}
|
| 12 |
.tiktok-fullscreen-feed{height:100%!important}
|
| 13 |
.tiktok-slide{height:80vh!important;max-height:680px!important;min-height:400px!important}
|
|
|
|
| 14 |
.tiktok-fullscreen-container:not(.shorts-feed-mode) .tiktok-video{object-fit:cover!important}
|
| 15 |
.tiktok-fullscreen-container.contain-mode .tiktok-video{object-fit:contain!important}
|
| 16 |
.tiktok-fullheight-btn{position:absolute;bottom:70px;right:12px;background:rgba(255,255,255,.15);color:#fff;border:none;padding:6px 12px;border-radius:16px;font-size:11px;cursor:pointer;z-index:8;backdrop-filter:blur(6px)}
|
|
|
|
| 30 |
if "shorts-feed-mode" not in app.CSS: app.CSS += SHORTS_EXTRA_CSS
|
| 31 |
if "FULLWIDTH" not in app.CSS: app.CSS += FULLWIDTH_CSS
|
| 32 |
|
|
|
|
| 33 |
def _fixed_extract(url):
|
| 34 |
vid=_extract_24h_video_url(url)
|
| 35 |
if not vid:return []
|
|
|
|
| 59 |
_orig_vp=app.render_video_page_html
|
| 60 |
def _pvp():
|
| 61 |
html=_orig_vp()
|
|
|
|
| 62 |
btn='<button class="tiktok-fullheight-btn" onclick="event.stopPropagation();var c=this.closest(\'.tiktok-fullscreen-container\');c.classList.toggle(\'contain-mode\');var on=c.classList.contains(\'contain-mode\');c.querySelectorAll(\'.tiktok-fullheight-btn\').forEach(function(b){b.classList.toggle(\'active\',on);b.textContent=on?\'\\u2922 1:1\':\'\\u2B07 Contain\';});">\u2B07 Contain</button>'
|
| 63 |
return html.replace('<div class="tiktok-unmute-hint"',btn+'<div class="tiktok-unmute-hint"')
|
| 64 |
app.render_video_page_html=_pvp
|
|
|
|
| 79 |
return [{"title":a["title"]+(f" (P{i+1})" if len(ps)>1 else ""),"link":a["link"],"img":a.get("img",""),"src":p,"poster":v["poster"],"vtype":"hls"} for i,p in enumerate(ps)]
|
| 80 |
with ThreadPoolExecutor(8) as ex:
|
| 81 |
results=list(ex.map(_f,arts))
|
| 82 |
+
vids=[item for r in results if r for item in r]
|
|
|
|
|
|
|
| 83 |
if vids:
|
| 84 |
ca=app.make_id(url);ordered=[v for v in vids if app.make_id(v["link"])==ca]+[v for v in vids if app.make_id(v["link"])!=ca]
|
| 85 |
return _btk(ordered,"24h")
|
|
|
|
| 96 |
return [{"title":a["title"],"link":a["link"],"img":a.get("img",""),"src":e["mp4"],"poster":e.get("poster",""),"vtype":"mp4"}]
|
| 97 |
with ThreadPoolExecutor(6) as ex:
|
| 98 |
results=list(ex.map(_fb,bl))
|
| 99 |
+
vids=[item for r in results if r for item in r]
|
|
|
|
|
|
|
| 100 |
if vids:
|
| 101 |
ca=app.make_id(url);ordered=[v for v in vids if app.make_id(v["link"])==ca]+[v for v in vids if app.make_id(v["link"])!=ca]
|
| 102 |
return _btk(ordered,"BDP")
|
|
|
|
| 104 |
|
| 105 |
def _btk(ordered,sl):
|
| 106 |
badge="bdp-badge-24h" if sl=="24h" else "bdp-badge-bdp"
|
| 107 |
+
is_shorts=sl=="24h"
|
| 108 |
slides=[];cards=[]
|
| 109 |
for vi,v in enumerate(ordered):
|
| 110 |
po=app.safe_url(v.get("poster",""));pa=f' poster="{po}"' if po else ""
|
|
|
|
| 112 |
orig_url=app.esc(v["link"])
|
| 113 |
sj=f"event.stopPropagation();window.bdpShareOriginal('{app.esc(v['title'])}','{orig_url}')"
|
| 114 |
vt=f'<video class="tiktok-video" playsinline preload="metadata"{pa} data-hls-src="{vs}" muted loop></video>' if v.get("vtype")=="hls" else f'<video class="tiktok-video" playsinline preload="metadata"{pa} muted loop><source src="{app.safe_url(vs)}" type="video/mp4"></video>'
|
|
|
|
| 115 |
cb='<button class="tiktok-fullheight-btn" onclick="event.stopPropagation();var c=this.closest(\'.tiktok-fullscreen-container\');c.classList.toggle(\'contain-mode\');var on=c.classList.contains(\'contain-mode\');c.querySelectorAll(\'.tiktok-fullheight-btn\').forEach(function(b){b.classList.toggle(\'active\',on);b.textContent=on?\'\\u2922 1:1\':\'\\u2B07 Contain\';});">\u2B07 Contain</button>'
|
| 116 |
slides.append(f'<div class="tiktok-slide" data-index="{vi}" data-aid="{aid}">{vt}<div class="tiktok-pause-icon">\u25b6</div><div class="tiktok-seek-controls"><button class="tiktok-seek-btn" onclick="event.stopPropagation();window.bdpSeek(this,-10)">\u23ea 10s</button><button class="tiktok-seek-btn" onclick="event.stopPropagation();window.bdpSeek(this,10)">10s \u23e9</button></div><div class="tiktok-bottom"><span class="bdp-badge {badge}">{sl}</span><p class="tiktok-title">{v["title"]}</p><div class="tiktok-actions"><button class="tiktok-action-btn" onclick="{sj}">\U0001f4e4 Chia s\u1ebb</button></div></div>{cb}<div class="tiktok-unmute-hint" onclick="window.bdpTikTokUnmute(this)">\U0001f507 B\u1eadt ti\u1ebfng</div><span class="tiktok-counter">{vi+1}/{len(ordered)}</span></div>')
|
| 117 |
img=app.safe_url(v.get("img")or v.get("poster",""))
|
| 118 |
+
# Click card scrolls to that slide index in the feed (no server reload)
|
| 119 |
+
cards.append(f'<div class="bdp-card" data-goto="{vi}" onclick="var f=document.querySelector(\'.tiktok-fullscreen-feed\');if(f){{var t=f.querySelectorAll(\'.tiktok-slide\')[{vi}];if(t){{t.scrollIntoView();window.scrollTo(0,0);}}}}">'
|
| 120 |
+
f'<div class="bdp-card-img"><img src="{img}" alt="" class="bdp-lazy-img"><div class="bdp-play-overlay">\u25b6</div></div>'
|
| 121 |
+
f'<div class="bdp-card-body"><span class="bdp-badge {badge}">{sl}</span><h3 class="bdp-card-title">{v["title"]}</h3></div></div>')
|
| 122 |
grid=f'<div class="bdp-wrap"><div class="bdp-topbar"><span>\U0001f3ac Video</span><span>{len(ordered)} video</span></div><div class="bdp-grid">{"".join(cards)}</div></div>'
|
| 123 |
mode_class="shorts-feed-mode" if is_shorts else ""
|
| 124 |
return f'<div class="tiktok-fullscreen-container {mode_class}" id="tiktok-fullscreen-feed"><div class="tiktok-fullscreen-feed">{"".join(slides)}</div></div>{grid}'
|