Spaces:
Running
Running
feat: unique vid per TikTok slide, share+hash per video, fix multi-part match all parts in feed
Browse files
app.py
CHANGED
|
@@ -587,16 +587,17 @@ def render_video_page_html():
|
|
| 587 |
src_label = "24h" if v.get("source")=="24h" else "BDP"
|
| 588 |
badge_cls = "bdp-badge-24h" if v.get("source")=="24h" else "bdp-badge-bdp"
|
| 589 |
aid = make_id(v.get("link",""))
|
|
|
|
| 590 |
sl = slug(v.get("title",""))
|
| 591 |
open_js = f"event.stopPropagation();window.bdpOpen('{esc(v.get('link',''))}','{aid}','{sl}')"
|
| 592 |
-
share_js = f"event.stopPropagation();window.
|
| 593 |
|
| 594 |
if vtype == "hls":
|
| 595 |
video_tag = f'<video class="tiktok-video" playsinline preload="metadata"{poster_attr} data-hls-src="{vsrc}" muted loop></video>'
|
| 596 |
else:
|
| 597 |
video_tag = f'<video class="tiktok-video" playsinline preload="metadata"{poster_attr} muted loop><source src="{safe_url(vsrc)}" type="video/mp4"></video>'
|
| 598 |
|
| 599 |
-
slides.append(f'''<div class="tiktok-slide" data-index="{vi}" data-aid="{aid}">
|
| 600 |
{video_tag}
|
| 601 |
<div class="tiktok-pause-icon">▶</div>
|
| 602 |
<div class="tiktok-seek-controls">
|
|
@@ -608,7 +609,7 @@ def render_video_page_html():
|
|
| 608 |
<p class="tiktok-title">{v.get("title","")}</p>
|
| 609 |
<div class="tiktok-actions">
|
| 610 |
<button class="tiktok-action-btn" onclick="{open_js}">📰 Đọc</button>
|
| 611 |
-
<button class="tiktok-action-btn" onclick="{share_js}">📤</button>
|
| 612 |
</div>
|
| 613 |
</div>
|
| 614 |
<div class="tiktok-unmute-hint" onclick="window.bdpTikTokUnmute(this)">🔇 Nhấn để bật tiếng</div>
|
|
@@ -907,6 +908,17 @@ window.bdpShareHash=async function(title,sl,aid){
|
|
| 907 |
document.body.removeChild(ta);
|
| 908 |
};
|
| 909 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 910 |
window.bdpToast=function(m){var e=document.getElementById('bdp-toast');if(!e){e=document.createElement('div');e.id='bdp-toast';e.className='bdp-toast';document.body.appendChild(e);}e.innerText=m;e.classList.add('show');setTimeout(function(){e.classList.remove('show');},2200);};
|
| 911 |
|
| 912 |
window.bdpSlideScroll=function(dir,trackId){
|
|
@@ -978,7 +990,7 @@ window.bdpOpenTikTok=function(url,aid){
|
|
| 978 |
var slides=feed.querySelectorAll('.tiktok-slide');
|
| 979 |
var targetIdx=-1;
|
| 980 |
slides.forEach(function(sl,i){
|
| 981 |
-
if(sl.getAttribute('data-aid')===aid) targetIdx=i;
|
| 982 |
});
|
| 983 |
if(targetIdx>=0 && slides[targetIdx]){
|
| 984 |
slides[targetIdx].scrollIntoView({behavior:'smooth'});
|
|
@@ -1104,7 +1116,35 @@ setInterval(function(){
|
|
| 1104 |
},1500);
|
| 1105 |
|
| 1106 |
var hh=window.location.hash;
|
| 1107 |
-
if(hh&&hh.startsWith('#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1108 |
/* Category hash: #cat/video, #cat/thoi-su, etc. */
|
| 1109 |
var catSlug=hh.slice(5);
|
| 1110 |
var catMap={""" + ",".join(f"'{ci[2]}':'{esc(CAT_KEYS[i])}'" for i,ci in enumerate(CAT_ICONS)) + """};
|
|
|
|
| 587 |
src_label = "24h" if v.get("source")=="24h" else "BDP"
|
| 588 |
badge_cls = "bdp-badge-24h" if v.get("source")=="24h" else "bdp-badge-bdp"
|
| 589 |
aid = make_id(v.get("link",""))
|
| 590 |
+
vid = make_id(vsrc)
|
| 591 |
sl = slug(v.get("title",""))
|
| 592 |
open_js = f"event.stopPropagation();window.bdpOpen('{esc(v.get('link',''))}','{aid}','{sl}')"
|
| 593 |
+
share_js = f"event.stopPropagation();window.bdpShareTikTok('{title_esc}','{sl}','{vid}')"
|
| 594 |
|
| 595 |
if vtype == "hls":
|
| 596 |
video_tag = f'<video class="tiktok-video" playsinline preload="metadata"{poster_attr} data-hls-src="{vsrc}" muted loop></video>'
|
| 597 |
else:
|
| 598 |
video_tag = f'<video class="tiktok-video" playsinline preload="metadata"{poster_attr} muted loop><source src="{safe_url(vsrc)}" type="video/mp4"></video>'
|
| 599 |
|
| 600 |
+
slides.append(f'''<div class="tiktok-slide" data-index="{vi}" data-aid="{aid}" data-vid="{vid}">
|
| 601 |
{video_tag}
|
| 602 |
<div class="tiktok-pause-icon">▶</div>
|
| 603 |
<div class="tiktok-seek-controls">
|
|
|
|
| 609 |
<p class="tiktok-title">{v.get("title","")}</p>
|
| 610 |
<div class="tiktok-actions">
|
| 611 |
<button class="tiktok-action-btn" onclick="{open_js}">📰 Đọc</button>
|
| 612 |
+
<button class="tiktok-action-btn" onclick="{share_js}">📤 Chia sẻ</button>
|
| 613 |
</div>
|
| 614 |
</div>
|
| 615 |
<div class="tiktok-unmute-hint" onclick="window.bdpTikTokUnmute(this)">🔇 Nhấn để bật tiếng</div>
|
|
|
|
| 908 |
document.body.removeChild(ta);
|
| 909 |
};
|
| 910 |
|
| 911 |
+
window.bdpShareTikTok=async function(title,sl,vid){
|
| 912 |
+
var hashUrl=SPACE_BASE+'/#/v/'+sl+'/'+vid;
|
| 913 |
+
var sd={title:title,url:hashUrl,text:title};
|
| 914 |
+
if(navigator.share){try{await navigator.share(sd);return;}catch(e){if(e.name==='AbortError')return;}}
|
| 915 |
+
if(navigator.clipboard&&navigator.clipboard.writeText){try{await navigator.clipboard.writeText(hashUrl);window.bdpToast('Đã sao chép liên kết!');return;}catch(e){}}
|
| 916 |
+
var ta=document.createElement('textarea');ta.value=hashUrl;ta.style.cssText='position:fixed;opacity:0';
|
| 917 |
+
document.body.appendChild(ta);ta.focus();ta.select();
|
| 918 |
+
try{document.execCommand('copy');window.bdpToast('Đã sao chép liên kết!');}catch(e){window.prompt('Sao chép:',hashUrl);}
|
| 919 |
+
document.body.removeChild(ta);
|
| 920 |
+
};
|
| 921 |
+
|
| 922 |
window.bdpToast=function(m){var e=document.getElementById('bdp-toast');if(!e){e=document.createElement('div');e.id='bdp-toast';e.className='bdp-toast';document.body.appendChild(e);}e.innerText=m;e.classList.add('show');setTimeout(function(){e.classList.remove('show');},2200);};
|
| 923 |
|
| 924 |
window.bdpSlideScroll=function(dir,trackId){
|
|
|
|
| 990 |
var slides=feed.querySelectorAll('.tiktok-slide');
|
| 991 |
var targetIdx=-1;
|
| 992 |
slides.forEach(function(sl,i){
|
| 993 |
+
if(targetIdx<0 && sl.getAttribute('data-aid')===aid) targetIdx=i;
|
| 994 |
});
|
| 995 |
if(targetIdx>=0 && slides[targetIdx]){
|
| 996 |
slides[targetIdx].scrollIntoView({behavior:'smooth'});
|
|
|
|
| 1116 |
},1500);
|
| 1117 |
|
| 1118 |
var hh=window.location.hash;
|
| 1119 |
+
if(hh&&hh.startsWith('#/v/')){
|
| 1120 |
+
/* TikTok video hash: #/v/slug/vid - switch to video page then scroll to vid */
|
| 1121 |
+
var vps=hh.slice(4).split('/');
|
| 1122 |
+
var vid=vps[vps.length-1];
|
| 1123 |
+
setTimeout(function(){
|
| 1124 |
+
/* Switch to Video category */
|
| 1125 |
+
window._bdpSetCat('\U0001f3ac Video T\u1ed5ng H\u1ee3p');
|
| 1126 |
+
document.querySelectorAll('.cat-icon-btn').forEach(function(b){
|
| 1127 |
+
b.classList.toggle('active',b.getAttribute('data-hash')==='video');
|
| 1128 |
+
});
|
| 1129 |
+
/* Poll for TikTok feed to appear then scroll to matching vid */
|
| 1130 |
+
var att=0;
|
| 1131 |
+
var ff=setInterval(function(){
|
| 1132 |
+
att++;
|
| 1133 |
+
var feed=document.querySelector('.tiktok-fullscreen-feed');
|
| 1134 |
+
if(feed){
|
| 1135 |
+
clearInterval(ff);
|
| 1136 |
+
var slides=feed.querySelectorAll('.tiktok-slide');
|
| 1137 |
+
slides.forEach(function(sl,i){
|
| 1138 |
+
if(sl.getAttribute('data-vid')===vid){
|
| 1139 |
+
sl.scrollIntoView({behavior:'smooth'});
|
| 1140 |
+
}
|
| 1141 |
+
});
|
| 1142 |
+
window.scrollTo({top:0,behavior:'smooth'});
|
| 1143 |
+
}
|
| 1144 |
+
if(att>30) clearInterval(ff);
|
| 1145 |
+
},300);
|
| 1146 |
+
},1500);
|
| 1147 |
+
} else if(hh&&hh.startsWith('#cat/')){
|
| 1148 |
/* Category hash: #cat/video, #cat/thoi-su, etc. */
|
| 1149 |
var catSlug=hh.slice(5);
|
| 1150 |
var catMap={""" + ",".join(f"'{ci[2]}':'{esc(CAT_KEYS[i])}'" for i,ci in enumerate(CAT_ICONS)) + """};
|