Spaces:
Running
Running
Upload static/app_v2.js
Browse files- static/app_v2.js +18 -20
static/app_v2.js
CHANGED
|
@@ -166,7 +166,7 @@ async function makeShortVideo(postId, btn, voice, speed, emotion){
|
|
| 166 |
const selectedVoiceBtn = container.querySelector('.tts-voice-btn.selected') ||
|
| 167 |
container.querySelector('.tts-voice-btn[style*="5cb87a"]') ||
|
| 168 |
container.querySelector('.tts-voice-btn');
|
| 169 |
-
voice = selectedVoiceBtn ? selectedVoiceBtn.dataset.voice : '
|
| 170 |
}
|
| 171 |
if(!emotion){
|
| 172 |
const selectedEmotionBtn = container.querySelector('.tts-emotion-btn.selected') ||
|
|
@@ -177,7 +177,7 @@ async function makeShortVideo(postId, btn, voice, speed, emotion){
|
|
| 177 |
const speedSelect = container.querySelector('.tts-speed');
|
| 178 |
speed = speedSelect ? parseFloat(speedSelect.value) || 1.2 : (speed || 1.2);
|
| 179 |
} else {
|
| 180 |
-
voice = voice || '
|
| 181 |
emotion = emotion || 'neutral';
|
| 182 |
speed = speed || 1.2;
|
| 183 |
}
|
|
@@ -243,19 +243,17 @@ function refreshShortAISlider(){
|
|
| 243 |
|
| 244 |
// === VOICE + EMOTION SYSTEM ===
|
| 245 |
var VOICE_LIST = [
|
| 246 |
-
{id:'
|
| 247 |
-
{id:'
|
| 248 |
-
{id:'
|
| 249 |
-
{id:'
|
| 250 |
-
{id:'
|
| 251 |
-
{id:'
|
| 252 |
-
{id:'
|
| 253 |
-
{id:'
|
| 254 |
-
{id:'
|
| 255 |
-
{id:'
|
| 256 |
-
{id:'
|
| 257 |
-
{id:'sunhee', label:'🎙️ SunHee (KO)', lang:'ko'},
|
| 258 |
-
{id:'xiaochen', label:'🎙️ XiaoChen (ZH)', lang:'zh'},
|
| 259 |
];
|
| 260 |
var EMOTION_LIST = [
|
| 261 |
{id:'neutral', label:'😐 Trung tính'},
|
|
@@ -297,7 +295,7 @@ function detectEmotion(text){
|
|
| 297 |
for(var em in kws){var score=0; for(var kw of kws[em]){if(t.indexOf(kw)>=0) score++;} if(score>bestScore){bestScore=score;bestEmotion=em;}}
|
| 298 |
return bestEmotion;
|
| 299 |
}
|
| 300 |
-
function getAutoVoice(lang){var map={vi:'
|
| 301 |
function buildVoiceEmotionSelector(post){
|
| 302 |
var lang=post.language||detectLanguage(post.title+' '+(post.text||''));
|
| 303 |
var autoVoice=post.voice||getAutoVoice(lang);
|
|
@@ -412,7 +410,7 @@ window.showVoiceEmotionSelector=function(postId,title,text){
|
|
| 412 |
try{
|
| 413 |
var speed=parseFloat(box.querySelector('#ve-speed').value)||1.2;
|
| 414 |
// FIX: Store selections in _ttsSelections for consistency
|
| 415 |
-
if(!_ttsSelections["inline-"+postId]) _ttsSelections["inline-"+postId]={voice:"
|
| 416 |
_ttsSelections["inline-"+postId].voice=selectedVoice;
|
| 417 |
_ttsSelections["inline-"+postId].emotion=selectedEmotion;
|
| 418 |
var r=await fetch('/api/ai/short/'+encodeURIComponent(postId),{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({voice:selectedVoice,emotion:selectedEmotion,speed:speed})});
|
|
@@ -544,7 +542,7 @@ async function toggleComments(videoId,idx){const panel=document.getElementById('
|
|
| 544 |
function renderInlineComments(panel,videoId,idx,cmts){let h='<div class="inline-cmt-header"><span>💬 Bình luận</span><button onclick="document.getElementById(\'cmt-inline-'+idx+'\').style.display=\'none\'">✕</button></div><div class="inline-cmt-list">';if(cmts.length){cmts.slice(-30).forEach(c=>{h+=`<div class="inline-cmt-item"><span class="inline-cmt-time">${c.time||''}</span>${esc(c.text)}</div>`;});}else{h+='<div style="color:#777;font-size:11px;padding:4px">Chưa có bình luận</div>';}h+=`</div><div class="inline-cmt-input"><input id="cmt-input-${idx}" placeholder="Viết bình luận..." onkeydown="if(event.key==='Enter')submitInlineCmt('${esc(videoId)}',${idx})"><button onclick="submitInlineCmt('${esc(videoId)}',${idx})">Gửi</button></div>`;panel.innerHTML=h;const list=panel.querySelector('.inline-cmt-list');if(list)list.scrollTop=list.scrollHeight;}
|
| 545 |
async function submitInlineCmt(videoId,idx){const inp=document.getElementById('cmt-input-'+idx);if(!inp)return;const text=inp.value.trim();if(!text)return;inp.value='';inp.disabled=true;const cmts=await postComment(videoId,text);inp.disabled=false;const panel=document.getElementById('cmt-inline-'+idx);if(panel)renderInlineComments(panel,videoId,idx,cmts);const cc=document.getElementById('cc-'+idx);if(cc)cc.textContent=fmtNum(cmts.length);}
|
| 546 |
function initTikTokFeed(){const feed=document.getElementById('tiktok-feed');if(!feed)return;const slides=feed.querySelectorAll('.tiktok-slide');let cur=-1;function act(i){if(i===cur)return;slides.forEach((sl,idx)=>{const v=sl.querySelector('video');const fr=sl.querySelector('iframe');if(idx===i){if(v&&v.dataset.hls&&!v._hls&&typeof Hls!=='undefined'&&Hls.isSupported()){const hls=new Hls();hls.loadSource(v.dataset.hls);hls.attachMedia(v);hls.on(Hls.Events.MANIFEST_PARSED,()=>v.play().catch(()=>{}));v._hls=hls}else if(v)v.play().catch(()=>{});if(fr&&!fr.src&&fr.dataset.ytSrc)fr.src=fr.dataset.ytSrc;const vid=sl.dataset.vid;if(vid&&!sl._viewed){sl._viewed=true;doInteract(vid,'view').then(j=>{if(j){const vc=document.getElementById('vc-'+idx);if(vc)vc.textContent=fmtNum(j.views);}});}}else{if(v){v.pause();if(v._hls){v._hls.destroy();v._hls=null}}if(fr&&fr.src)fr.src=''}});cur=i}let sT;feed.addEventListener('scroll',()=>{clearTimeout(sT);sT=setTimeout(()=>{const rect=feed.getBoundingClientRect(),ctr=rect.top+rect.height/2;let best=-1,bestD=1e9;slides.forEach((sl,i)=>{const d=Math.abs(sl.getBoundingClientRect().top+sl.getBoundingClientRect().height/2-ctr);if(d<bestD){bestD=d;best=i}});if(best>=0)act(best)},150)});setTimeout(()=>act(0),400);slides.forEach(sl=>{const v=sl.querySelector('video');if(v)v.addEventListener('click',e=>{e.preventDefault();v.paused?v.play().catch(()=>{}):v.pause()})});const ids=[...slides].map(sl=>sl.dataset.vid||'');loadCounters(ids)}
|
| 547 |
-
async function openHighlightFeed(league,idx,forceUrl){showView('view-tiktok');const el=document.getElementById('view-tiktok');el.innerHTML='<div class="loading">Đang tải...</div>';let articles=(_hlLeagueData||{})[league]||[];if(!articles.length){try{articles=await fetch('/api/highlights/'+league).then(r=>r.json())}catch(e){articles=[]}}if(!articles.length){el.innerHTML='<div class="loading">Không có video</div>';return}const vids=[];const results=await Promise.all(articles.map(async(a,i)=>{try{const r=await fetch('/api/video_url?url='+encodeURIComponent(a.link));const v=await r.json();if(v&&v.src)return{..
|
| 548 |
async function openYTShortsFeed(startIdx){showView('view-tiktok');const el=document.getElementById('view-tiktok');el.innerHTML='<div class="loading">Đang tải...</div>';const arts=_shortsData.length?_shortsData:await fetch('/api/shorts').then(r=>r.json()).catch(()=>[]);if(!arts.length){el.innerHTML='<div class="loading">Không có shorts</div>';return}const ordered=startIdx>0?[...arts.slice(startIdx),...arts.slice(0,startIdx)]:arts;let h=`<button class="back-btn" onclick="switchCat('home')">← Shorts</button><div class="tiktok-container"><div class="tiktok-feed" id="tiktok-feed">`;ordered.forEach((v,i)=>{const id=v.id||'';const src=`https://www.youtube.com/embed/${id}?autoplay=1&rel=0&playsinline=1`;const vtag=`<iframe data-yt-src="${src}" allowfullscreen allow="accelerometer;autoplay;clipboard-write;encrypted-media;gyroscope;picture-in-picture"></iframe>`;const badge=v.channel==='baosuckhoedoisongboyte'?'SKĐS':'Dân trí';const videoId='yt-'+id;h+=buildTikTokSlide({vtag,title:v.title,badge,badgeClass:'badge-fpt',videoId,idx:i,total:ordered.length,shareUrl:'https://youtube.com/watch?v='+id});});h+='</div></div>';el.innerHTML=h;initTikTokFeed();}
|
| 549 |
async function openShortAIFeed(startIdx){showView('view-tiktok');const el=document.getElementById('view-tiktok');el.innerHTML='<div class="loading">Đang tải...</div>';const wall=(await fetch('/api/wall').then(r=>r.json()).catch(()=>({posts:[]}))).posts||[];const vids=wall.filter(p=>p.video);if(!vids.length){el.innerHTML='<div class="loading">Chưa có Short AI</div>';return}const ordered=startIdx>0?[...vids.slice(startIdx),...vids.slice(0,startIdx)]:vids;let h=`<button class="back-btn" onclick="switchCat('home')">← Short AI</button><div class="tiktok-container"><div class="tiktok-feed" id="tiktok-feed">`;ordered.forEach((p,i)=>{const vtag=`<video src="${p.video}" playsinline loop controls></video>`;const videoId='ai-'+(p.id||i);h+=buildTikTokSlide({vtag,title:p.title,badge:'AI',badgeClass:'badge-ai',videoId,idx:i,total:ordered.length,shareUrl:SPACE});});h+='</div></div>';el.innerHTML=h;initTikTokFeed();}
|
| 550 |
async function readArticle(url){showView('view-article');const el=document.getElementById('view-article');el.innerHTML='<div class="loading">Đang tải...</div>';try{const r=await fetch('/api/article?url='+encodeURIComponent(url));const data=await r.json();if(data&&!data.error&&data.body&&data.body.length){_currentArticle={url,data};let h=`<button class="back-btn" onclick="switchCat('home')">← Quay lại</button><div class="article-view"><h1 class="article-title">${esc(data.title)}</h1>`;if(data.summary)h+=`<div class="article-summary">${esc(data.summary)}</div>`;const seen={};data.body.forEach(b=>{if(b.type==='p')h+=`<p class="article-p">${b.text}</p>`;else if(b.type==='img'&&b.src&&!seen[b.src]){seen[b.src]=1;h+=`<img class="article-img" src="${esc(b.src)}" onerror="this.style.display='none'">`}else if(b.type==='heading')h+=`<h2 class="article-h2">${esc(b.text)}</h2>`});h+=`<div class="article-actions"><button class="primary" onclick="rewriteArticle()">🤖 Rewrite AI đăng tường</button><button onclick="doShare('${esc(data.title)}','${esc(url)}','${esc(data.og_image||'')}')">📤</button><button onclick="window.open('${esc(url)}','_blank')">🔗 Gốc</button></div><div class="article-ai-ask"><h3 style="font-size:14px;color:#5cb87a">🤖 Hỏi AI</h3><textarea id="ask-q" placeholder="Hỏi về bài viết..."></textarea><button onclick="askAI()">Hỏi</button><div id="ask-a" class="article-ai-answer"></div></div></div>`;el.innerHTML=h;window.scrollTo(0,0);return;}}catch(e){}el.innerHTML=`<button class="back-btn" onclick="switchCat('home')">← Quay lại</button><div class="loading"><p>Không đọc được.</p><a href="${esc(url)}" target="_blank" style="color:#5cb87a">Mở gốc →</a></div>`;}
|
|
@@ -576,7 +574,7 @@ async function readWallPost(i){
|
|
| 576 |
if(!hasVideo){
|
| 577 |
voiceEmotionHtml = buildVoiceEmotionSelector(p);
|
| 578 |
} else {
|
| 579 |
-
voiceEmotionHtml = '<div style="margin-top:10px;padding:10px;background:#1a2a1f;border-radius:8px;font-size:11px;color:#5cb87a;">🎬 Voice: '+(p.voice||'
|
| 580 |
// REMOVED: buildVoiceEmotionSelector(p) call - do not show selector for posts with existing Short AI
|
| 581 |
}
|
| 582 |
document.getElementById('view-article').innerHTML =
|
|
@@ -639,4 +637,4 @@ if (document.readyState === 'loading') {
|
|
| 639 |
});
|
| 640 |
} else {
|
| 641 |
if (typeof loadHome === 'function') loadHome();
|
| 642 |
-
}
|
|
|
|
| 166 |
const selectedVoiceBtn = container.querySelector('.tts-voice-btn.selected') ||
|
| 167 |
container.querySelector('.tts-voice-btn[style*="5cb87a"]') ||
|
| 168 |
container.querySelector('.tts-voice-btn');
|
| 169 |
+
voice = selectedVoiceBtn ? selectedVoiceBtn.dataset.voice : 'vi-VN-HoaiMyNeural';
|
| 170 |
}
|
| 171 |
if(!emotion){
|
| 172 |
const selectedEmotionBtn = container.querySelector('.tts-emotion-btn.selected') ||
|
|
|
|
| 177 |
const speedSelect = container.querySelector('.tts-speed');
|
| 178 |
speed = speedSelect ? parseFloat(speedSelect.value) || 1.2 : (speed || 1.2);
|
| 179 |
} else {
|
| 180 |
+
voice = voice || 'vi-VN-HoaiMyNeural';
|
| 181 |
emotion = emotion || 'neutral';
|
| 182 |
speed = speed || 1.2;
|
| 183 |
}
|
|
|
|
| 243 |
|
| 244 |
// === VOICE + EMOTION SYSTEM ===
|
| 245 |
var VOICE_LIST = [
|
| 246 |
+
{id:'vi-VN-HoaiMyNeural', label:'🎙️ Hoài My (VI)', lang:'vi'},
|
| 247 |
+
{id:'vi-VN-NamMinhNeural', label:'🎙️ Nam Minh (VI)', lang:'vi'},
|
| 248 |
+
{id:'en-US-AndrewMultilingualNeural', label:'🎙️ Andrew (EN)', lang:'en'},
|
| 249 |
+
{id:'en-AU-WilliamMultilingualNeural', label:'🎙️ William (EN)', lang:'en'},
|
| 250 |
+
{id:'pt-BR-ThalitaMultilingualNeural', label:'🎙️ Thalita (PT)', lang:'pt'},
|
| 251 |
+
{id:'fr-FR-VivienneMultilingualNeural', label:'🎙️ Vivienne (FR)', lang:'fr'},
|
| 252 |
+
{id:'fr-FR-RemyMultilingualNeural', label:'🎙️ Rémy (FR)', lang:'fr'},
|
| 253 |
+
{id:'de-DE-SeraphinaMultilingualNeural', label:'🎙️ Seraphina (DE)', lang:'de'},
|
| 254 |
+
{id:'de-DE-FlorianMultilingualNeural', label:'🎙️ Florian (DE)', lang:'de'},
|
| 255 |
+
{id:'ko-KR-HyunsuMultilingualNeural', label:'🎙️ Hyunsu (KO)', lang:'ko'},
|
| 256 |
+
{id:'it-IT-GiuseppeMultilingualNeural', label:'🎙️ Giuseppe (IT)', lang:'it'},
|
|
|
|
|
|
|
| 257 |
];
|
| 258 |
var EMOTION_LIST = [
|
| 259 |
{id:'neutral', label:'😐 Trung tính'},
|
|
|
|
| 295 |
for(var em in kws){var score=0; for(var kw of kws[em]){if(t.indexOf(kw)>=0) score++;} if(score>bestScore){bestScore=score;bestEmotion=em;}}
|
| 296 |
return bestEmotion;
|
| 297 |
}
|
| 298 |
+
function getAutoVoice(lang){var map={vi:'vi-VN-HoaiMyNeural',pt:'pt-BR-ThalitaMultilingualNeural',en:'en-US-AndrewMultilingualNeural',fr:'fr-FR-VivienneMultilingualNeural',de:'de-DE-SeraphinaMultilingualNeural',ko:'ko-KR-HyunsuMultilingualNeural',it:'it-IT-GiuseppeMultilingualNeural'};return map[lang]||'vi-VN-HoaiMyNeural';}
|
| 299 |
function buildVoiceEmotionSelector(post){
|
| 300 |
var lang=post.language||detectLanguage(post.title+' '+(post.text||''));
|
| 301 |
var autoVoice=post.voice||getAutoVoice(lang);
|
|
|
|
| 410 |
try{
|
| 411 |
var speed=parseFloat(box.querySelector('#ve-speed').value)||1.2;
|
| 412 |
// FIX: Store selections in _ttsSelections for consistency
|
| 413 |
+
if(!_ttsSelections["inline-"+postId]) _ttsSelections["inline-"+postId]={voice:"vi-VN-HoaiMyNeural",emotion:"neutral"};
|
| 414 |
_ttsSelections["inline-"+postId].voice=selectedVoice;
|
| 415 |
_ttsSelections["inline-"+postId].emotion=selectedEmotion;
|
| 416 |
var r=await fetch('/api/ai/short/'+encodeURIComponent(postId),{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({voice:selectedVoice,emotion:selectedEmotion,speed:speed})});
|
|
|
|
| 542 |
function renderInlineComments(panel,videoId,idx,cmts){let h='<div class="inline-cmt-header"><span>💬 Bình luận</span><button onclick="document.getElementById(\'cmt-inline-'+idx+'\').style.display=\'none\'">✕</button></div><div class="inline-cmt-list">';if(cmts.length){cmts.slice(-30).forEach(c=>{h+=`<div class="inline-cmt-item"><span class="inline-cmt-time">${c.time||''}</span>${esc(c.text)}</div>`;});}else{h+='<div style="color:#777;font-size:11px;padding:4px">Chưa có bình luận</div>';}h+=`</div><div class="inline-cmt-input"><input id="cmt-input-${idx}" placeholder="Viết bình luận..." onkeydown="if(event.key==='Enter')submitInlineCmt('${esc(videoId)}',${idx})"><button onclick="submitInlineCmt('${esc(videoId)}',${idx})">Gửi</button></div>`;panel.innerHTML=h;const list=panel.querySelector('.inline-cmt-list');if(list)list.scrollTop=list.scrollHeight;}
|
| 543 |
async function submitInlineCmt(videoId,idx){const inp=document.getElementById('cmt-input-'+idx);if(!inp)return;const text=inp.value.trim();if(!text)return;inp.value='';inp.disabled=true;const cmts=await postComment(videoId,text);inp.disabled=false;const panel=document.getElementById('cmt-inline-'+idx);if(panel)renderInlineComments(panel,videoId,idx,cmts);const cc=document.getElementById('cc-'+idx);if(cc)cc.textContent=fmtNum(cmts.length);}
|
| 544 |
function initTikTokFeed(){const feed=document.getElementById('tiktok-feed');if(!feed)return;const slides=feed.querySelectorAll('.tiktok-slide');let cur=-1;function act(i){if(i===cur)return;slides.forEach((sl,idx)=>{const v=sl.querySelector('video');const fr=sl.querySelector('iframe');if(idx===i){if(v&&v.dataset.hls&&!v._hls&&typeof Hls!=='undefined'&&Hls.isSupported()){const hls=new Hls();hls.loadSource(v.dataset.hls);hls.attachMedia(v);hls.on(Hls.Events.MANIFEST_PARSED,()=>v.play().catch(()=>{}));v._hls=hls}else if(v)v.play().catch(()=>{});if(fr&&!fr.src&&fr.dataset.ytSrc)fr.src=fr.dataset.ytSrc;const vid=sl.dataset.vid;if(vid&&!sl._viewed){sl._viewed=true;doInteract(vid,'view').then(j=>{if(j){const vc=document.getElementById('vc-'+idx);if(vc)vc.textContent=fmtNum(j.views);}});}}else{if(v){v.pause();if(v._hls){v._hls.destroy();v._hls=null}}if(fr&&fr.src)fr.src=''}});cur=i}let sT;feed.addEventListener('scroll',()=>{clearTimeout(sT);sT=setTimeout(()=>{const rect=feed.getBoundingClientRect(),ctr=rect.top+rect.height/2;let best=-1,bestD=1e9;slides.forEach((sl,i)=>{const d=Math.abs(sl.getBoundingClientRect().top+sl.getBoundingClientRect().height/2-ctr);if(d<bestD){bestD=d;best=i}});if(best>=0)act(best)},150)});setTimeout(()=>act(0),400);slides.forEach(sl=>{const v=sl.querySelector('video');if(v)v.addEventListener('click',e=>{e.preventDefault();v.paused?v.play().catch(()=>{}):v.pause()})});const ids=[...slides].map(sl=>sl.dataset.vid||'');loadCounters(ids)}
|
| 545 |
+
async function openHighlightFeed(league,idx,forceUrl){showView('view-tiktok');const el=document.getElementById('view-tiktok');el.innerHTML='<div class="loading">Đang tải...</div>';let articles=(_hlLeagueData||{})[league]||[];if(!articles.length){try{articles=await fetch('/api/highlights/'+league).then(r=>r.json())}catch(e){articles=[]}}if(!articles.length){el.innerHTML='<div class="loading">Không có video</div>';return}const vids=[];const results=await Promise.all(articles.map(async(a,i)=>{try{const r=await fetch('/api/video_url?url='+encodeURIComponent(a.link)+'&img='+encodeURIComponent(a.img||''));const v=await r.json();if(v&&v.src){return{_idx:i,title:a.title||v.title||'',link:a.link||'',img:a.img||v.poster||'',src:v.src,type:v.type||'',poster:v.poster||a.img||''}}return null}catch(e){}return null}));results.forEach(r=>{if(r)vids.push(r)});vids.sort((a,b)=>a._idx-b._idx);if(!vids.length){el.innerHTML='<div class="loading">Không tìm thấy video</div>';return}let ti=vids.findIndex(v=>v._idx===idx);if(ti<0)ti=0;const ordered=ti>0?[...vids.slice(ti),...vids.slice(0,ti)]:vids;let h=`<button class="back-btn" onclick="switchCat('home')">← Highlight</button><div class="tiktok-container"><div class="tiktok-feed" id="tiktok-feed">`;ordered.forEach((v,i)=>{const isYT=v.type==='youtube',isHLS=!isYT&&v.src?.includes('.m3u8'),poster=v.poster?` poster="${esc(v.poster)}"`:'';const vtag=isYT?`<iframe data-yt-src="${v.src}" allowfullscreen allow="accelerometer;autoplay;clipboard-write;encrypted-media;gyroscope;picture-in-picture"></iframe>`:isHLS?`<video playsinline preload="none"${poster} data-hls="${v.src}" loop controls></video>`:`<video playsinline preload="none"${poster} loop controls><source src="${v.src}" type="video/mp4"></video>`;const videoId='hl-'+league+'-'+v._idx;h+=buildTikTokSlide({vtag,title:v.title,badge:'HL',badgeClass:'badge-fpt',videoId,idx:i,total:ordered.length,shareUrl:v.link||'',extraBtn:`<button class="tiktok-right-btn" onclick="event.stopPropagation();this.closest('.tiktok-slide').classList.toggle('ratio-wide')"><div class="icon">⬜</div><div class="count">16:9</div></button>`});});h+='</div></div>';el.innerHTML=h;initTikTokFeed();}
|
| 546 |
async function openYTShortsFeed(startIdx){showView('view-tiktok');const el=document.getElementById('view-tiktok');el.innerHTML='<div class="loading">Đang tải...</div>';const arts=_shortsData.length?_shortsData:await fetch('/api/shorts').then(r=>r.json()).catch(()=>[]);if(!arts.length){el.innerHTML='<div class="loading">Không có shorts</div>';return}const ordered=startIdx>0?[...arts.slice(startIdx),...arts.slice(0,startIdx)]:arts;let h=`<button class="back-btn" onclick="switchCat('home')">← Shorts</button><div class="tiktok-container"><div class="tiktok-feed" id="tiktok-feed">`;ordered.forEach((v,i)=>{const id=v.id||'';const src=`https://www.youtube.com/embed/${id}?autoplay=1&rel=0&playsinline=1`;const vtag=`<iframe data-yt-src="${src}" allowfullscreen allow="accelerometer;autoplay;clipboard-write;encrypted-media;gyroscope;picture-in-picture"></iframe>`;const badge=v.channel==='baosuckhoedoisongboyte'?'SKĐS':'Dân trí';const videoId='yt-'+id;h+=buildTikTokSlide({vtag,title:v.title,badge,badgeClass:'badge-fpt',videoId,idx:i,total:ordered.length,shareUrl:'https://youtube.com/watch?v='+id});});h+='</div></div>';el.innerHTML=h;initTikTokFeed();}
|
| 547 |
async function openShortAIFeed(startIdx){showView('view-tiktok');const el=document.getElementById('view-tiktok');el.innerHTML='<div class="loading">Đang tải...</div>';const wall=(await fetch('/api/wall').then(r=>r.json()).catch(()=>({posts:[]}))).posts||[];const vids=wall.filter(p=>p.video);if(!vids.length){el.innerHTML='<div class="loading">Chưa có Short AI</div>';return}const ordered=startIdx>0?[...vids.slice(startIdx),...vids.slice(0,startIdx)]:vids;let h=`<button class="back-btn" onclick="switchCat('home')">← Short AI</button><div class="tiktok-container"><div class="tiktok-feed" id="tiktok-feed">`;ordered.forEach((p,i)=>{const vtag=`<video src="${p.video}" playsinline loop controls></video>`;const videoId='ai-'+(p.id||i);h+=buildTikTokSlide({vtag,title:p.title,badge:'AI',badgeClass:'badge-ai',videoId,idx:i,total:ordered.length,shareUrl:SPACE});});h+='</div></div>';el.innerHTML=h;initTikTokFeed();}
|
| 548 |
async function readArticle(url){showView('view-article');const el=document.getElementById('view-article');el.innerHTML='<div class="loading">Đang tải...</div>';try{const r=await fetch('/api/article?url='+encodeURIComponent(url));const data=await r.json();if(data&&!data.error&&data.body&&data.body.length){_currentArticle={url,data};let h=`<button class="back-btn" onclick="switchCat('home')">← Quay lại</button><div class="article-view"><h1 class="article-title">${esc(data.title)}</h1>`;if(data.summary)h+=`<div class="article-summary">${esc(data.summary)}</div>`;const seen={};data.body.forEach(b=>{if(b.type==='p')h+=`<p class="article-p">${b.text}</p>`;else if(b.type==='img'&&b.src&&!seen[b.src]){seen[b.src]=1;h+=`<img class="article-img" src="${esc(b.src)}" onerror="this.style.display='none'">`}else if(b.type==='heading')h+=`<h2 class="article-h2">${esc(b.text)}</h2>`});h+=`<div class="article-actions"><button class="primary" onclick="rewriteArticle()">🤖 Rewrite AI đăng tường</button><button onclick="doShare('${esc(data.title)}','${esc(url)}','${esc(data.og_image||'')}')">📤</button><button onclick="window.open('${esc(url)}','_blank')">🔗 Gốc</button></div><div class="article-ai-ask"><h3 style="font-size:14px;color:#5cb87a">🤖 Hỏi AI</h3><textarea id="ask-q" placeholder="Hỏi về bài viết..."></textarea><button onclick="askAI()">Hỏi</button><div id="ask-a" class="article-ai-answer"></div></div></div>`;el.innerHTML=h;window.scrollTo(0,0);return;}}catch(e){}el.innerHTML=`<button class="back-btn" onclick="switchCat('home')">← Quay lại</button><div class="loading"><p>Không đọc được.</p><a href="${esc(url)}" target="_blank" style="color:#5cb87a">Mở gốc →</a></div>`;}
|
|
|
|
| 574 |
if(!hasVideo){
|
| 575 |
voiceEmotionHtml = buildVoiceEmotionSelector(p);
|
| 576 |
} else {
|
| 577 |
+
voiceEmotionHtml = '<div style="margin-top:10px;padding:10px;background:#1a2a1f;border-radius:8px;font-size:11px;color:#5cb87a;">🎬 Voice: '+(p.voice||'vi-VN-HoaiMyNeural')+' | 😊 Emotion: '+(p.emotion||'neutral')+' | ⚡ Speed: '+(p.short_speed||'1.2')+'x</div>';
|
| 578 |
// REMOVED: buildVoiceEmotionSelector(p) call - do not show selector for posts with existing Short AI
|
| 579 |
}
|
| 580 |
document.getElementById('view-article').innerHTML =
|
|
|
|
| 637 |
});
|
| 638 |
} else {
|
| 639 |
if (typeof loadHome === 'function') loadHome();
|
| 640 |
+
}
|