bep40 commited on
Commit
cb8a759
·
verified ·
1 Parent(s): 7b2cfdd

Fix duplicate topic input, knowledge topic prompt, and direct shorts playback

Browse files
Files changed (1) hide show
  1. ai_runtime_final5.py +73 -0
ai_runtime_final5.py ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Final5 runtime: remove duplicate topic box, improve Qwen topic knowledge output, fix Shorts direct playback."""
2
+ import re, time
3
+ from urllib.parse import quote
4
+ import ai_runtime_final4 as f4
5
+ from ai_runtime_final4 import app, base, rt, HTMLResponse, JSONResponse, Request, Query
6
+
7
+ # Remove topic/root endpoints to override.
8
+ _PATCH={('/api/topic_post','POST'),('/','GET')}
9
+ app.router.routes=[r for r in app.router.routes if not any(getattr(r,'path',None)==p and m in getattr(r,'methods',set()) for p,m in _PATCH)]
10
+
11
+ def clean(s):
12
+ import html as html_lib
13
+ return re.sub(r"\s+"," ",html_lib.unescape(s or "")).strip()
14
+
15
+ def _topic_image(topic):
16
+ try:return base.pollinations_image_url(topic)
17
+ except Exception:return "https://image.pollinations.ai/prompt/"+quote("Vietnamese educational editorial illustration "+topic)+"?width=1024&height=576&nologo=true"
18
+
19
+ @app.post('/api/topic_post')
20
+ async def topic_post_knowledge(request:Request):
21
+ body=await request.json();topic=clean(body.get('topic',''))
22
+ if not topic:return JSONResponse({'error':'missing topic'},status_code=400)
23
+ img=_topic_image(topic)
24
+ prompt=f"""Người dùng muốn đăng một bài trên Tường AI về chủ đề: "{topic}".
25
+
26
+ Hãy viết NGAY nội dung kiến thức/thông tin hữu ích về chủ đề đó, không lập dàn ý chung chung, không nói "có thể viết", không hướng dẫn cách viết.
27
+
28
+ Yêu cầu đầu ra:
29
+ - Tiêu đề hấp dẫn, cụ thể.
30
+ - 1 đoạn mở đầu giải thích trực tiếp chủ đề là gì/vì sao đáng chú ý.
31
+ - 5-7 đoạn hoặc ý chính cung cấp kiến thức thực chất, ví dụ, bối cảnh, tác động, hiểu lầm thường gặp, điểm cần lưu ý.
32
+ - Nếu chủ đề là thể thao, hãy nói về bối cảnh, nhân vật/đội bóng, ý nghĩa chiến thuật hoặc lịch sử liên quan.
33
+ - Nếu chủ đề là công nghệ/khoa học/xã hội, hãy giải thích khái niệm, ứng dụng, rủi ro/lợi ích, ví dụ thực tế.
34
+ - Không bịa số liệu thời sự mới; nếu không chắc, dùng cách nói thận trọng.
35
+ - Viết như bài đăng hoàn chỉnh để đọc được ngay.
36
+ - Cuối bài thêm: Nguồn tham khảo: Qwen2.5-VL / kiến thức tổng hợp.
37
+ """
38
+ text=await base.qwen_generate(prompt,image_url=img,max_tokens=1400)
39
+ if not text:
40
+ text=f"{topic}\n\n{topic} là một chủ đề có nhiều khía cạnh cần nhìn từ bối cảnh, ý nghĩa thực tế và tác động đối với người quan tâm. Bài viết này tóm lược các điểm quan trọng nhất để người đọc hiểu nhanh vấn đề, thay vì chỉ liệt kê tiêu đề hoặc dàn ý.\n\nNguồn tham khảo: Qwen2.5-VL / kiến thức tổng hợp."
41
+ post=base.make_post(topic,text,img,'','topic_qwen',sources=[{'title':'Qwen2.5-VL / kiến thức tổng hợp','url':'','via':'Qwen2.5-VL'}])
42
+ post['images']=[img]
43
+ posts=base._load_ai_wall();posts.insert(0,post);base._save_ai_wall(posts)
44
+ return JSONResponse({'post':post})
45
+
46
+ FINAL5_INJECT=r'''
47
+ <style>
48
+ /* Keep exactly one topic input */
49
+ #ai-topic-input-final3,.ai-compose-row.topic-final3,#ai-topic-input-final4,.topic-final4{display:none!important}.topic-final5{display:flex!important;flex-direction:column!important;gap:8px!important;width:100%!important;margin-top:6px}.topic-final5 input,.topic-final5 button{display:block!important;width:100%!important;box-sizing:border-box!important}.topic-final5 button{background:#2d8659!important;color:#fff!important;border:0!important;border-radius:18px!important;padding:9px 12px!important;font-size:11px!important;font-weight:700!important}
50
+ </style>
51
+ <script>
52
+ (function(){
53
+ function esc(s){return String(s||'').replace(/[&<>"']/g,m=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[m]));}
54
+ let shortsFinal5=[];
55
+ function removeDuplicateTopicBoxes(){document.querySelectorAll('#ai-topic-input-final3,.topic-final3,#ai-topic-input-final4,.topic-final4').forEach(e=>{let row=e.closest('.topic-final3,.topic-final4,.ai-compose-row')||e;e.remove?row.remove():row.style.display='none'});let comp=document.querySelector('.ai-compose');if(!comp)return;if(!document.getElementById('ai-topic-input-final5')){let row=document.createElement('div');row.className='topic-final5';row.innerHTML='<input id="ai-topic-input-final5" placeholder="Bạn muốn AI viết kiến thức về chủ đề gì? Ví dụ: thần đồng Arsenal, AI trong giáo dục, biến đổi khí hậu..."><button id="ai-topic-btn-final5" onclick="createTopicPostFinal5()">✨ Tạo bài kiến thức bằng Qwen</button>';comp.insertBefore(row,comp.firstChild.nextSibling);} }
56
+ window.createTopicPostFinal5=async function(){let inp=document.getElementById('ai-topic-input-final5');let topic=(inp&&inp.value||'').trim();if(!topic)return alert('Nhập chủ đề trước');let btn=document.getElementById('ai-topic-btn-final5');if(btn){btn.disabled=true;btn.textContent='Đang tạo bài...'}try{let r=await fetch('/api/topic_post',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({topic})});let j=await r.json();if(!r.ok||j.error)throw new Error(j.error||'Lỗi');if(window.finalWall)window.finalWall.unshift(j.post);if(window.finalWall3)window.finalWall3.unshift(j.post);if(inp)inp.value='';if(window.renderWall)window.renderWall();if(window.renderAIShortHome)window.renderAIShortHome();alert('Đã tạo bài kiến thức và đăng lên Tường AI.');}catch(e){alert(e.message)}finally{if(btn){btn.disabled=false;btn.textContent='✨ Tạo bài kiến thức bằng Qwen'}}};
57
+ async function loadShortsFinal5(){shortsFinal5=await fetch('/api/shorts?refresh=1').then(r=>r.json()).catch(()=>[]);return shortsFinal5;}
58
+ function actionPanel(kind,id){return `<div class="short-action-panel"><button class="short-action-btn" onclick="shortAct('${kind}','${id}','view')"><div class="ico">👁</div><span id="v-${kind}-${id}">0</span></button><button class="short-action-btn" onclick="shortAct('${kind}','${id}','like')"><div class="ico">❤️</div><span id="l-${kind}-${id}">0</span></button><button class="short-action-btn" onclick="openCommentBox('${kind}','${id}')"><div class="ico">💬</div><span>BL</span></button><button class="short-action-btn" onclick="openAskBox('${kind}','${id}')"><div class="ico">🤖</div><span>Hỏi</span></button><button class="short-action-btn" onclick="shareShortCtx('${kind}','${id}')"><div class="ico">📤</div><span>Share</span></button></div>`}
59
+ window.openShortsFinal5=async function(startIdx){let arts=shortsFinal5.length?shortsFinal5:await loadShortsFinal5();if(!arts.length)return alert('Chưa tải được Shorts');let ordered=startIdx>0?arts.slice(startIdx).concat(arts.slice(0,startIdx)):arts;showView('view-tiktok');let h='<button class="back-btn" onclick="switchCat(\'home\')">← Shorts Dân trí & SKĐS</button><div class="tiktok-container"><div class="tiktok-feed" id="tiktok-feed">';ordered.forEach((v,i)=>{let id=v.id||((v.link||'').match(/v=([A-Za-z0-9_-]{11})/)||[])[1]||String(i);let src='https://www.youtube.com/embed/'+id+'?autoplay=1&rel=0&playsinline=1';h+=`<div class="tiktok-slide" data-kind="yt" data-id="${id}" data-title="${esc(v.title)}" data-channel="${esc(v.channel||'')}"><iframe data-yt-src="${src}" allowfullscreen allow="accelerometer;autoplay;clipboard-write;encrypted-media;gyroscope;picture-in-picture"></iframe><div class="tiktok-bottom"><span class="badge badge-fpt">YT</span><p class="tiktok-title">${esc(v.title)}</p></div>${actionPanel('yt',id)}<span class="tiktok-counter">${i+1}/${ordered.length}</span></div>`});h+='</div></div>';document.getElementById('view-tiktok').innerHTML=h;initShortsFeedFinal5();}
60
+ function initShortsFeedFinal5(){let feed=document.getElementById('tiktok-feed');if(!feed)return;let slides=feed.querySelectorAll('.tiktok-slide');let cur=-1;function act(i){if(i===cur)return;slides.forEach((sl,idx)=>{let fr=sl.querySelector('iframe');let v=sl.querySelector('video');if(idx===i){if(fr&&!fr.src&&fr.dataset.ytSrc)fr.src=fr.dataset.ytSrc;if(v)v.play().catch(()=>{});shortAct(sl.dataset.kind,sl.dataset.id,'view').catch(()=>{})}else{if(fr&&fr.src)fr.src='';if(v)v.pause();}});cur=i}let t;feed.addEventListener('scroll',()=>{clearTimeout(t);t=setTimeout(()=>{let rect=feed.getBoundingClientRect(),ctr=rect.top+rect.height/2,b=-1,d=1e9;slides.forEach((sl,i)=>{let dd=Math.abs(sl.getBoundingClientRect().top+sl.getBoundingClientRect().height/2-ctr);if(dd<d){d=dd;b=i}});if(b>=0)act(b)},130)});setTimeout(()=>act(0),250)}
61
+ function patchShortsHomeClick(){let home=document.getElementById('view-home');if(!home)return;document.querySelectorAll('#shorts-final4 .slider-item').forEach((el,i)=>{el.setAttribute('onclick',`openShortsFinal5(${i})`)});document.querySelectorAll('.slider-wrap .slider-label').forEach(label=>{if((label.textContent||'').includes('Shorts')){let wrap=label.closest('.slider-wrap');wrap?.querySelectorAll('.slider-item').forEach((el,i)=>el.setAttribute('onclick',`openShortsFinal5(${i})`));}})}
62
+ let oldOpen=window.openTikTok;window.openTikTok=function(type,startIdx){if(type==='shorts')return openShortsFinal5(startIdx||0);return oldOpen?oldOpen(type,startIdx):null;};
63
+ // Make YouTube ask AI receive title/channel from slide dataset.
64
+ let oldShortAct=window.shortAct;window.shortAct=async function(kind,id,action,text=''){let slide=document.querySelector(`.tiktok-slide[data-id="${id}"]`);let title=slide?.dataset.title||'';let channel=slide?.dataset.channel||'';let body={id,kind:kind==='yt'?'yt':kind,action,text,title,context:title?('Video Shorts YouTube từ kênh '+channel+'. Tiêu đề: '+title):''};let r=await fetch('/api/ai/interact',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)});let j=await r.json();let st=j.stats||j;let v=document.getElementById(`v-${kind}-${id}`),l=document.getElementById(`l-${kind}-${id}`);if(v&&st.views!=null)v.textContent=st.views;if(l&&st.likes!=null)l.textContent=st.likes;return st;};
65
+ setTimeout(async()=>{removeDuplicateTopicBoxes();await loadShortsFinal5();patchShortsHomeClick();},900);setInterval(()=>{removeDuplicateTopicBoxes();patchShortsHomeClick();},1800);
66
+ })();
67
+ </script>
68
+ '''
69
+
70
+ @app.get('/')
71
+ async def index_final5():
72
+ html=f4.f3.f2.f1._load_index_html();body=getattr(rt.old,'PATCH_INJECT','')+f4.f3.f2.f1.FINAL_INJECT+f4.f3.FINAL3_INJECT+f4.FINAL4_INJECT+FINAL5_INJECT
73
+ return HTMLResponse(html.replace('</body>',body+'\n</body>') if '</body>' in html else html+body)