bep40 commited on
Commit
c1e2703
·
verified ·
1 Parent(s): 79fa1c8

Background prefetch on startup + instant cache response for homepage speed"

Browse files
Files changed (1) hide show
  1. ai_runtime_patch_fast.py +94 -11
ai_runtime_patch_fast.py CHANGED
@@ -1,14 +1,97 @@
1
- """Patch over b9b6ba4 final6: faster home, source thumbnails, rewrite button, in-app source links, topic quality."""
2
- import re
3
  import ai_runtime_final6 as f6
4
  from ai_runtime_final6 import app, rt, f5, HTMLResponse, JSONResponse, Request, Query
5
  import html as html_lib
6
 
7
  def clean(s):return re.sub(r"\s+"," ",html_lib.unescape(str(s or ""))).strip()
8
 
9
- # Override topic_post to produce cleaner, more focused content.
10
- app.router.routes=[r for r in app.router.routes if not ((getattr(r,'path',None)=='/api/topic_post' and 'POST' in getattr(r,'methods',set())) or (getattr(r,'path',None)=='/' and 'GET' in getattr(r,'methods',set())))]
 
 
11
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  @app.post('/api/topic_post')
13
  async def topic_post_focused(request:Request):
14
  body=await request.json();topic=clean(body.get('topic',''))
@@ -51,33 +134,33 @@ QUY TẮC BẮT BUỘC:
51
  posts=f5.base._load_ai_wall();posts.insert(0,post);f5.base._save_ai_wall(posts)
52
  return JSONResponse({'post':post,'sources_count':len(details or sources)})
53
 
 
54
  PATCH_INJECT=r'''
55
  <script>
56
  (function(){
57
  function esc(s){return String(s||'').replace(/[&<>"']/g,m=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[m]));}
58
- // 1) Faster home: defer hot_topics and shorts refresh to after main content loads.
59
  let _hotLoaded=false;
60
- function deferHotTopics(){if(_hotLoaded)return;_hotLoaded=true;setTimeout(()=>{if(typeof ensureHotTopics==='function')ensureHotTopics();if(typeof ensureNewsShortsHome==='function')ensureNewsShortsHome();},3000);}
61
  if(document.readyState==='complete')deferHotTopics();else window.addEventListener('load',deferHotTopics);
62
 
63
- // 2) Source links in topic posts: open in-app via readArticle instead of external link.
64
  function patchSourceLinksInApp(){document.querySelectorAll('.source-detail-item a[href]').forEach(a=>{if(a.dataset.inapp)return;a.dataset.inapp='1';let url=a.href;a.removeAttribute('target');a.onclick=function(e){e.preventDefault();if(typeof readArticle==='function')readArticle(url);else window.open(url,'_blank');};a.textContent='📖 Đọc trên VNEWS';a.style.cursor='pointer';});}
65
 
66
  // 3) Source items show thumbnail.
67
  function patchTopicSourceImages(){document.querySelectorAll('.source-detail-item').forEach(el=>{if(el.dataset.imgPatched)return;el.dataset.imgPatched='1';let a=el.querySelector('a[href]');if(!a||!a.href)return;let url=a.getAttribute('href')||a.href;fetch('/api/article?url='+encodeURIComponent(url)).then(r=>r.json()).then(d=>{if(d&&(d.og_image||d.img)){let img=document.createElement('img');img.style.cssText='width:100%;aspect-ratio:16/9;object-fit:cover;border-radius:6px;margin-bottom:6px;background:#222';img.loading='lazy';img.src=d.og_image||d.img;img.onerror=function(){this.style.display='none'};el.prepend(img);}}).catch(()=>{});});}
68
 
69
- // 4) Add rewrite button to every article view.
70
  function addRewriteButton(){let art=document.querySelector('#view-article .article-view');if(!art||art.querySelector('.rewrite-injected'))return;let actions=art.querySelector('.article-actions');if(!actions){actions=document.createElement('div');actions.className='article-actions';art.appendChild(actions);}if(actions.querySelector('[data-rewrite]'))return;let btn=document.createElement('button');btn.className='primary rewrite-injected';btn.setAttribute('data-rewrite','1');btn.textContent='🤖 AI viết lại & đăng tường';btn.onclick=function(){if(typeof rewriteCurrentArticle==='function')rewriteCurrentArticle();else alert('Chức năng rewrite chưa sẵn sàng.')};actions.insertBefore(btn,actions.firstChild);}
71
 
72
- // 5) Patch readLiveTopicWall to add rewrite + in-app source links.
73
  let oldReadLive=window.readLiveTopicWall;
74
  if(oldReadLive){window.readLiveTopicWall=function(){let ret=oldReadLive.apply(this,arguments);setTimeout(()=>{addRewriteButton();patchSourceLinksInApp();patchTopicSourceImages();},600);return ret;}}
75
 
76
- // 6) Also patch readArticle to add rewrite after render.
77
  let oldReadArticle=window.readArticle;
78
  if(oldReadArticle){window.readArticle=async function(){let ret=await oldReadArticle.apply(this,arguments);setTimeout(addRewriteButton,600);return ret;}}
79
 
80
- // Run periodically.
81
  setInterval(()=>{addRewriteButton();patchSourceLinksInApp();patchTopicSourceImages();},1500);
82
  setTimeout(addRewriteButton,800);
83
  })();
 
1
+ """Patch over b9b6ba4 final6: faster home via background prefetch, source thumbnails, rewrite button, in-app links, topic quality."""
2
+ import re, threading, time
3
  import ai_runtime_final6 as f6
4
  from ai_runtime_final6 import app, rt, f5, HTMLResponse, JSONResponse, Request, Query
5
  import html as html_lib
6
 
7
  def clean(s):return re.sub(r"\s+"," ",html_lib.unescape(str(s or ""))).strip()
8
 
9
+ # ===== BACKGROUND PREFETCH ON STARTUP =====
10
+ _bg_home_cache={"t":0,"d":[]}
11
+ _bg_shorts_cache={"t":0,"d":[]}
12
+ _bg_loading=False
13
 
14
+ def _bg_refresh():
15
+ global _bg_loading
16
+ if _bg_loading:return
17
+ _bg_loading=True
18
+ try:
19
+ # Homepage via fast RSS (already defined in f6 as _fast_homepage)
20
+ if hasattr(f6,'_fast_homepage'):
21
+ data=f6._fast_homepage()
22
+ if data:_bg_home_cache.update({"t":time.time(),"d":data})
23
+ # Shorts
24
+ if hasattr(f6,'api_shorts_final6'):
25
+ # Call internal function directly
26
+ raw=[]
27
+ for h in f6.YOUTUBE_HANDLES:
28
+ raw.extend(f6._yt_ytdlp(h,20) or f6._yt_html(h,20))
29
+ raw.extend(f6._fallback_shorts())
30
+ seen=set();out=[]
31
+ for v in raw:
32
+ vid=v.get('id') or ''
33
+ if not vid:continue
34
+ if vid in seen:continue
35
+ seen.add(vid);out.append(v)
36
+ if out:_bg_shorts_cache.update({"t":time.time(),"d":out[:40]})
37
+ except Exception:pass
38
+ finally:_bg_loading=False
39
+
40
+ def _start_bg_prefetch():
41
+ t=threading.Thread(target=_bg_refresh,daemon=True)
42
+ t.start()
43
+
44
+ @app.on_event("startup")
45
+ async def startup_prefetch():
46
+ _start_bg_prefetch()
47
+
48
+ # Periodic background refresh every 10 minutes.
49
+ def _periodic_refresh():
50
+ while True:
51
+ time.sleep(600)
52
+ try:_bg_refresh()
53
+ except Exception:pass
54
+
55
+ _periodic_thread=threading.Thread(target=_periodic_refresh,daemon=True)
56
+ _periodic_thread.start()
57
+
58
+ # ===== OVERRIDE HOMEPAGE & SHORTS FOR INSTANT RESPONSE =====
59
+ app.router.routes=[r for r in app.router.routes if not (
60
+ (getattr(r,'path',None)=='/api/homepage' and 'GET' in getattr(r,'methods',set())) or
61
+ (getattr(r,'path',None)=='/api/shorts' and 'GET' in getattr(r,'methods',set())) or
62
+ (getattr(r,'path',None)=='/api/topic_post' and 'POST' in getattr(r,'methods',set())) or
63
+ (getattr(r,'path',None)=='/' and 'GET' in getattr(r,'methods',set()))
64
+ )]
65
+
66
+ @app.get('/api/homepage')
67
+ def api_homepage_instant():
68
+ # Return cache immediately; if stale, trigger background refresh.
69
+ now=time.time()
70
+ if _bg_home_cache['d']:
71
+ if now-_bg_home_cache['t']>300:
72
+ threading.Thread(target=_bg_refresh,daemon=True).start()
73
+ return JSONResponse(_bg_home_cache['d'])
74
+ # First call, no cache yet: try fast RSS inline (fast, 4s timeout each).
75
+ if hasattr(f6,'_fast_homepage'):
76
+ data=f6._fast_homepage()
77
+ if data:
78
+ _bg_home_cache.update({"t":now,"d":data})
79
+ return JSONResponse(data)
80
+ return JSONResponse([])
81
+
82
+ @app.get('/api/shorts')
83
+ def api_shorts_instant(refresh:int=Query(default=0)):
84
+ now=time.time()
85
+ if _bg_shorts_cache['d'] and (not refresh or now-_bg_shorts_cache['t']<120):
86
+ if now-_bg_shorts_cache['t']>600:
87
+ threading.Thread(target=_bg_refresh,daemon=True).start()
88
+ return JSONResponse(_bg_shorts_cache['d'])
89
+ # Inline fallback if no cache.
90
+ if hasattr(f6,'api_shorts_final6'):
91
+ return f6.api_shorts_final6(refresh=refresh)
92
+ return JSONResponse([])
93
+
94
+ # ===== TOPIC POST (focused, no irrelevant content) =====
95
  @app.post('/api/topic_post')
96
  async def topic_post_focused(request:Request):
97
  body=await request.json();topic=clean(body.get('topic',''))
 
134
  posts=f5.base._load_ai_wall();posts.insert(0,post);f5.base._save_ai_wall(posts)
135
  return JSONResponse({'post':post,'sources_count':len(details or sources)})
136
 
137
+ # ===== FRONTEND PATCH =====
138
  PATCH_INJECT=r'''
139
  <script>
140
  (function(){
141
  function esc(s){return String(s||'').replace(/[&<>"']/g,m=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[m]));}
142
+ // 1) Defer non-critical fetches.
143
  let _hotLoaded=false;
144
+ function deferHotTopics(){if(_hotLoaded)return;_hotLoaded=true;setTimeout(()=>{if(typeof ensureHotTopics==='function')ensureHotTopics();if(typeof ensureNewsShortsHome==='function')ensureNewsShortsHome();},4000);}
145
  if(document.readyState==='complete')deferHotTopics();else window.addEventListener('load',deferHotTopics);
146
 
147
+ // 2) Source links open in-app.
148
  function patchSourceLinksInApp(){document.querySelectorAll('.source-detail-item a[href]').forEach(a=>{if(a.dataset.inapp)return;a.dataset.inapp='1';let url=a.href;a.removeAttribute('target');a.onclick=function(e){e.preventDefault();if(typeof readArticle==='function')readArticle(url);else window.open(url,'_blank');};a.textContent='📖 Đọc trên VNEWS';a.style.cursor='pointer';});}
149
 
150
  // 3) Source items show thumbnail.
151
  function patchTopicSourceImages(){document.querySelectorAll('.source-detail-item').forEach(el=>{if(el.dataset.imgPatched)return;el.dataset.imgPatched='1';let a=el.querySelector('a[href]');if(!a||!a.href)return;let url=a.getAttribute('href')||a.href;fetch('/api/article?url='+encodeURIComponent(url)).then(r=>r.json()).then(d=>{if(d&&(d.og_image||d.img)){let img=document.createElement('img');img.style.cssText='width:100%;aspect-ratio:16/9;object-fit:cover;border-radius:6px;margin-bottom:6px;background:#222';img.loading='lazy';img.src=d.og_image||d.img;img.onerror=function(){this.style.display='none'};el.prepend(img);}}).catch(()=>{});});}
152
 
153
+ // 4) Add rewrite button.
154
  function addRewriteButton(){let art=document.querySelector('#view-article .article-view');if(!art||art.querySelector('.rewrite-injected'))return;let actions=art.querySelector('.article-actions');if(!actions){actions=document.createElement('div');actions.className='article-actions';art.appendChild(actions);}if(actions.querySelector('[data-rewrite]'))return;let btn=document.createElement('button');btn.className='primary rewrite-injected';btn.setAttribute('data-rewrite','1');btn.textContent='🤖 AI viết lại & đăng tường';btn.onclick=function(){if(typeof rewriteCurrentArticle==='function')rewriteCurrentArticle();else alert('Chức năng rewrite chưa sẵn sàng.')};actions.insertBefore(btn,actions.firstChild);}
155
 
156
+ // 5) Patch readLiveTopicWall.
157
  let oldReadLive=window.readLiveTopicWall;
158
  if(oldReadLive){window.readLiveTopicWall=function(){let ret=oldReadLive.apply(this,arguments);setTimeout(()=>{addRewriteButton();patchSourceLinksInApp();patchTopicSourceImages();},600);return ret;}}
159
 
160
+ // 6) Patch readArticle.
161
  let oldReadArticle=window.readArticle;
162
  if(oldReadArticle){window.readArticle=async function(){let ret=await oldReadArticle.apply(this,arguments);setTimeout(addRewriteButton,600);return ret;}}
163
 
 
164
  setInterval(()=>{addRewriteButton();patchSourceLinksInApp();patchTopicSourceImages();},1500);
165
  setTimeout(addRewriteButton,800);
166
  })();