bep40 commited on
Commit
bb74e32
·
verified ·
1 Parent(s): 519a0a3

Fix AI wall persistence on homepage and no reload for topic posts

Browse files
Files changed (1) hide show
  1. restore_patch_topic_shorts.py +11 -19
restore_patch_topic_shorts.py CHANGED
@@ -18,22 +18,17 @@ CACHE={'t':0,'d':[]}
18
  SHORT_CHANNELS=['baodantri7941','baosuckhoedoisongboyte']
19
  TEXT_MODELS=['Qwen/Qwen3-30B-A3B-Instruct-2507:fastest','Qwen/Qwen2.5-72B-Instruct:fastest','Qwen/Qwen2.5-7B-Instruct:fastest']
20
 
21
-
22
  def clean(s):
23
  import html
24
  return re.sub(r'\s+',' ',html.unescape(s or '')).strip()
25
 
26
-
27
  def _topic_image(topic):
28
  try:return base.pollinations_image_url(topic)
29
  except Exception:return 'https://image.pollinations.ai/prompt/'+quote('Vietnamese magazine editorial illustration '+topic)+'?width=1024&height=576&nologo=true'
30
 
31
  async def qwen_article(topic):
32
  token=os.environ.get('HF_TOKEN') or os.environ.get('HUGGINGFACEHUB_API_TOKEN') or ''
33
- system=("Bạn là biên tập viên tiếng Việt chuyên viết bài báo phổ thông chất lượng cao. "
34
- "Từ một chủ đề ngắn, viết thành bài hoàn chỉnh, tự nhiên, giàu thông tin. "
35
- "Không tự nhận là AI. Không viết kiểu 'Dưới đây là bài viết'. Không giải thích quy trình. "
36
- "Không bịa số liệu/tên nguồn/trích dẫn. Văn phong hiện đại, mạch lạc, có chiều sâu.")
37
  user=f"""Chủ đề: {topic}
38
 
39
  Hãy viết một bài tiếng Việt hoàn chỉnh khoảng 900-1400 từ.
@@ -62,12 +57,9 @@ Yêu cầu chất lượng:
62
  txt=re.sub(r'<think>.*?</think>\s*','',txt,flags=re.S).strip()
63
  if len(txt)>500:return txt
64
  except Exception:pass
65
- # fallback to app's qwen_generate if router unavailable
66
- prompt=system+'\n\n'+user
67
- txt=await base.qwen_generate(prompt,max_tokens=1800)
68
  return txt or (f"# {topic}\n\n{topic} là một chủ đề có nhiều lớp nghĩa cần được nhìn từ bối cảnh, tác động và ứng dụng thực tế. Bài viết này cung cấp góc nhìn tổng hợp, giúp người đọc hiểu rõ bản chất vấn đề thay vì chỉ nắm vài ý rời rạc.\n\nNguồn tham khảo: Qwen / kiến thức tổng hợp.")
69
 
70
-
71
  def ytdlp_shorts(handle,count=24):
72
  try:
73
  import yt_dlp
@@ -109,7 +101,6 @@ def fresh_shorts():
109
  if v['id'] not in seen:seen.add(v['id']);out.append(v)
110
  return out[:60]
111
 
112
- # remove conflicting routes
113
  app.router.routes=[r for r in app.router.routes if getattr(r,'path',None) not in {'/api/shorts','/api/topic_post','/'}]
114
 
115
  @app.get('/api/shorts')
@@ -122,31 +113,32 @@ def api_shorts(refresh:int=Query(default=0)):
122
  async def topic_post(request:Request):
123
  body=await request.json();topic=clean(body.get('topic',''))
124
  if not topic:return JSONResponse({'error':'missing topic'},status_code=400)
125
- img=_topic_image(topic)
126
- text=await qwen_article(topic)
127
  post=base.make_post(topic,text,img,'','topic_article',sources=[{'title':'Qwen text model / kiến thức tổng hợp','url':'','via':'Qwen'}]);post['images']=[img]
128
  posts=base._load_ai_wall();posts.insert(0,post);base._save_ai_wall(posts)
129
  return JSONResponse({'post':post})
130
 
131
  PATCH=r'''
132
- <style>#fixed-shorts-dedupe-note{display:none}.topic-final3,.topic-final4{display:none!important}.topic-final5{display:flex!important}</style>
133
  <script>
134
  (function(){
135
  function esc(s){return String(s||'').replace(/[&<>"']/g,m=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[m]));}
136
- let shorts=[];
137
  async function loadShorts(){shorts=await fetch('/api/shorts?refresh=1').then(r=>r.json()).catch(()=>[]);return shorts;}
 
 
 
138
  function dedupeShortSlides(){let found=false;document.querySelectorAll('.slider-wrap').forEach(w=>{let label=w.querySelector('.slider-label')?.textContent||'';if(label.includes('Shorts')){if(found)w.remove();else{found=true;w.id='main-shorts-wrap';w.querySelectorAll('.slider-item').forEach((el,i)=>el.setAttribute('onclick',`openShortsDedupe(${i})`));}}});}
139
  window.openShortsDedupe=async function(start){let data=shorts.length?shorts:await loadShorts();if(!data.length)return alert('Không tải được Shorts');let ordered=start>0?data.slice(start).concat(data.slice(0,start)):data;showView('view-tiktok');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)=>{let src='https://www.youtube.com/embed/'+v.id+'?autoplay=1&rel=0&playsinline=1';h+=`<div class="tiktok-slide"><iframe data-src="${src}" allowfullscreen allow="accelerometer;autoplay;clipboard-write;encrypted-media;gyroscope;picture-in-picture" style="width:100%;height:100%;border:0"></iframe><div class="tiktok-bottom"><span class="badge badge-fpt">YT</span><p class="tiktok-title">${esc(v.title)}</p></div><span class="tiktok-counter">${i+1}/${ordered.length}</span></div>`});h+='</div></div>';document.getElementById('view-tiktok').innerHTML=h;initShortFeed();}
140
  function initShortFeed(){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 f=sl.querySelector('iframe');if(idx===i){if(f&&!f.src)f.src=f.dataset.src}else if(f)f.src=''});cur=i}let t;feed.addEventListener('scroll',()=>{clearTimeout(t);t=setTimeout(()=>{let rect=feed.getBoundingClientRect(),ctr=rect.top+rect.height/2,b=0,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}});act(b)},120)});setTimeout(()=>act(0),200)}
141
  let oldOpen=window.openTikTok;window.openTikTok=function(type,idx){if(type==='shorts')return openShortsDedupe(idx||0);return oldOpen?oldOpen(type,idx):null};
142
- 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 viết 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(inp)inp.value='';alert('Đã tạo bài viết bằng model text Qwen chất lượng hơn.');location.reload();}catch(e){alert(e.message)}finally{if(btn){btn.disabled=false;btn.textContent='✨ Tạo bài kiến thức bằng Qwen'}}};
143
- setTimeout(async()=>{await loadShorts();dedupeShortSlides();},1000);setInterval(dedupeShortSlides,2000);
144
  })();
145
  </script>
146
  '''
147
 
148
  @app.get('/')
149
  async def index_patched():
150
- html=await restored.index_final6()
151
- text=html.body.decode('utf-8') if hasattr(html,'body') else str(html)
152
  return HTMLResponse(text.replace('</body>',PATCH+'\n</body>') if '</body>' in text else text+PATCH)
 
18
  SHORT_CHANNELS=['baodantri7941','baosuckhoedoisongboyte']
19
  TEXT_MODELS=['Qwen/Qwen3-30B-A3B-Instruct-2507:fastest','Qwen/Qwen2.5-72B-Instruct:fastest','Qwen/Qwen2.5-7B-Instruct:fastest']
20
 
 
21
  def clean(s):
22
  import html
23
  return re.sub(r'\s+',' ',html.unescape(s or '')).strip()
24
 
 
25
  def _topic_image(topic):
26
  try:return base.pollinations_image_url(topic)
27
  except Exception:return 'https://image.pollinations.ai/prompt/'+quote('Vietnamese magazine editorial illustration '+topic)+'?width=1024&height=576&nologo=true'
28
 
29
  async def qwen_article(topic):
30
  token=os.environ.get('HF_TOKEN') or os.environ.get('HUGGINGFACEHUB_API_TOKEN') or ''
31
+ system=("Bạn là biên tập viên tiếng Việt chuyên viết bài báo phổ thông chất lượng cao. Từ một chủ đề ngắn, viết thành bài hoàn chỉnh, tự nhiên, giàu thông tin. Không tự nhận là AI. Không viết kiểu 'Dưới đây là bài viết'. Không giải thích quy trình. Không bịa số liệu/tên nguồn/trích dẫn. Văn phong hiện đại, mạch lạc, có chiều sâu.")
 
 
 
32
  user=f"""Chủ đề: {topic}
33
 
34
  Hãy viết một bài tiếng Việt hoàn chỉnh khoảng 900-1400 từ.
 
57
  txt=re.sub(r'<think>.*?</think>\s*','',txt,flags=re.S).strip()
58
  if len(txt)>500:return txt
59
  except Exception:pass
60
+ txt=await base.qwen_generate(system+'\n\n'+user,max_tokens=1800)
 
 
61
  return txt or (f"# {topic}\n\n{topic} là một chủ đề có nhiều lớp nghĩa cần được nhìn từ bối cảnh, tác động và ứng dụng thực tế. Bài viết này cung cấp góc nhìn tổng hợp, giúp người đọc hiểu rõ bản chất vấn đề thay vì chỉ nắm vài ý rời rạc.\n\nNguồn tham khảo: Qwen / kiến thức tổng hợp.")
62
 
 
63
  def ytdlp_shorts(handle,count=24):
64
  try:
65
  import yt_dlp
 
101
  if v['id'] not in seen:seen.add(v['id']);out.append(v)
102
  return out[:60]
103
 
 
104
  app.router.routes=[r for r in app.router.routes if getattr(r,'path',None) not in {'/api/shorts','/api/topic_post','/'}]
105
 
106
  @app.get('/api/shorts')
 
113
  async def topic_post(request:Request):
114
  body=await request.json();topic=clean(body.get('topic',''))
115
  if not topic:return JSONResponse({'error':'missing topic'},status_code=400)
116
+ img=_topic_image(topic);text=await qwen_article(topic)
 
117
  post=base.make_post(topic,text,img,'','topic_article',sources=[{'title':'Qwen text model / kiến thức tổng hợp','url':'','via':'Qwen'}]);post['images']=[img]
118
  posts=base._load_ai_wall();posts.insert(0,post);base._save_ai_wall(posts)
119
  return JSONResponse({'post':post})
120
 
121
  PATCH=r'''
122
+ <style>.topic-final3,.topic-final4{display:none!important}.topic-final5{display:flex!important}.wall-live-fixed{margin:6px 4px;background:#1a1a1a;border:1px solid #2a2a2a;border-radius:8px;overflow:hidden}</style>
123
  <script>
124
  (function(){
125
  function esc(s){return String(s||'').replace(/[&<>"']/g,m=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[m]));}
126
+ let shorts=[];let wall=[];
127
  async function loadShorts(){shorts=await fetch('/api/shorts?refresh=1').then(r=>r.json()).catch(()=>[]);return shorts;}
128
+ async function loadWall(){wall=(await fetch('/api/ai_wall').then(r=>r.json()).catch(()=>({posts:[]}))).posts||[];renderWallFixed();return wall;}
129
+ function renderWallFixed(){let home=document.getElementById('view-home');if(!home)return;document.getElementById('wall-live-fixed')?.remove();if(!wall.length)return;let wrap=document.createElement('div');wrap.id='wall-live-fixed';wrap.className='wall-live-fixed';let h='<div class="slider-header"><span class="slider-label">🧱 Tường AI</span><span class="slider-note">Đã lưu</span></div><div class="slider-track">';wall.slice(0,30).forEach((p,i)=>{h+=`<div class="wall-item"><div class="wall-thumb">${p.img?`<img src="${p.img}">`:''}</div><div class="wall-title">${esc(p.title)}</div><div class="wall-text">${esc(p.text)}</div><div class="wall-actions"><button class="primary" onclick="readWallFixed(${i})">Xem</button></div></div>`});h+='</div>';wrap.innerHTML=h;let comp=document.querySelector('.ai-compose');if(comp)comp.after(wrap);else home.prepend(wrap);}
130
+ window.readWallFixed=function(i){let p=wall[i];if(!p)return;showView('view-article');document.getElementById('view-article').innerHTML=`<button class="back-btn" onclick="switchCat('home')">← Quay lại</button><div class="article-view"><span class="badge badge-ai">AI</span><h1 class="article-title">${esc(p.title)}</h1>${p.img?`<img class="article-img" src="${p.img}">`:''}<p class="article-p" style="white-space:pre-wrap">${esc(p.text)}</p></div>`;window.scrollTo(0,0)}
131
  function dedupeShortSlides(){let found=false;document.querySelectorAll('.slider-wrap').forEach(w=>{let label=w.querySelector('.slider-label')?.textContent||'';if(label.includes('Shorts')){if(found)w.remove();else{found=true;w.id='main-shorts-wrap';w.querySelectorAll('.slider-item').forEach((el,i)=>el.setAttribute('onclick',`openShortsDedupe(${i})`));}}});}
132
  window.openShortsDedupe=async function(start){let data=shorts.length?shorts:await loadShorts();if(!data.length)return alert('Không tải được Shorts');let ordered=start>0?data.slice(start).concat(data.slice(0,start)):data;showView('view-tiktok');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)=>{let src='https://www.youtube.com/embed/'+v.id+'?autoplay=1&rel=0&playsinline=1';h+=`<div class="tiktok-slide"><iframe data-src="${src}" allowfullscreen allow="accelerometer;autoplay;clipboard-write;encrypted-media;gyroscope;picture-in-picture" style="width:100%;height:100%;border:0"></iframe><div class="tiktok-bottom"><span class="badge badge-fpt">YT</span><p class="tiktok-title">${esc(v.title)}</p></div><span class="tiktok-counter">${i+1}/${ordered.length}</span></div>`});h+='</div></div>';document.getElementById('view-tiktok').innerHTML=h;initShortFeed();}
133
  function initShortFeed(){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 f=sl.querySelector('iframe');if(idx===i){if(f&&!f.src)f.src=f.dataset.src}else if(f)f.src=''});cur=i}let t;feed.addEventListener('scroll',()=>{clearTimeout(t);t=setTimeout(()=>{let rect=feed.getBoundingClientRect(),ctr=rect.top+rect.height/2,b=0,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}});act(b)},120)});setTimeout(()=>act(0),200)}
134
  let oldOpen=window.openTikTok;window.openTikTok=function(type,idx){if(type==='shorts')return openShortsDedupe(idx||0);return oldOpen?oldOpen(type,idx):null};
135
+ 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 viết 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');wall.unshift(j.post);renderWallFixed();readWallFixed(0);if(inp)inp.value='';alert('Đã tạo bài viết bằng model text Qwen chất lượng hơn.');}catch(e){alert(e.message)}finally{if(btn){btn.disabled=false;btn.textContent='✨ Tạo bài kiến thức bằng Qwen'}}};
136
+ setTimeout(async()=>{await loadShorts();await loadWall();dedupeShortSlides();},1000);setInterval(()=>{dedupeShortSlides();if(document.getElementById('view-home')?.classList.contains('active'))renderWallFixed();},2500);
137
  })();
138
  </script>
139
  '''
140
 
141
  @app.get('/')
142
  async def index_patched():
143
+ html=await restored.index_final6();text=html.body.decode('utf-8') if hasattr(html,'body') else str(html)
 
144
  return HTMLResponse(text.replace('</body>',PATCH+'\n</body>') if '</body>' in text else text+PATCH)