File size: 6,705 Bytes
ff7aefb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
"""Final patch: homepage fix + AI topics at top + SSE auto-update"""
import re, json, time
from fastapi.responses import HTMLResponse, JSONResponse
from fastapi import Query

# Import chain - must be after ai_runtime_final6
try:
    import ai_runtime_final6 as f6
    from ai_runtime_final6 import app, f5
    from main import rt
except Exception as e:
    print(f"[ERROR] f6 import: {e}")
    f6 = None
    f5 = None
    rt = None

PATCH_CSS_JS = r'''
<style>
.short-cmt-panel{position:fixed;bottom:0;left:0;right:0;max-height:55vh;background:#181818;border-radius:16px 16px 0 0;z-index:99999;padding:14px;display:none;overflow-y:auto}.short-cmt-panel.active{display:block}.short-cmt-panel textarea{width:100%;background:#222;border:1px solid #444;color:#eee;border-radius:10px;padding:9px;margin:6px 0;min-height:60px}.short-cmt-panel button{background:#2d8659;border:0;color:#fff;border-radius:10px;padding:8px 12px;margin:4px}.cmt-item{background:#222;border-radius:8px;padding:7px;margin:5px 0;color:#ccc;font-size:12px}
.storage-warn{background:#332200;border:1px solid #664400;color:#ffcc00;padding:8px 12px;border-radius:8px;font-size:11px;margin:6px 0}
button[onclick*="rewriteCurrentArticle"]{display:none!important}
#ai-short-home,.ai-short-home,.ai-short-card-final{display:none!important}
</style>
<div id="short-cmt-panel" class="short-cmt-panel"></div>
<script>
(function(){
function esc(s){return String(s||'').replace(/[&<>"']/g,m=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[m]));}
fetch('/api/storage_status').then(r=>r.json()).then(j=>{if(!j.persistent){let h=document.getElementById('view-home');if(h){let w=document.createElement('div');w.className='storage-warn';w.innerHTML='⚠️ Persistent Storage chưa bật. Bật: Space Settings → Persistent Storage → Small.';h.prepend(w);}}});

// ===== AI HOT TOPICS PREPEND =====
const AI_HOT_TOPICS = ['Công nghệ AI', 'World Cup 2026', 'Kinh tế Việt Nam', 'Bóng đá châu Âu'];
async function ensureHotTopics(){let inp=document.getElementById('ai-topic-input-final5');if(!inp||document.getElementById('hot-topic-row-ai'))return;let row=document.createElement('div');row.id='hot-topic-row-ai';row.style.cssText='display:flex;gap:6px;overflow-x:auto;padding:4px 0;margin:6px 0';let topics=[];try{let j=await fetch('/api/hot_topics').then(r=>r.json()).catch(()=>({topics:[]}));topics=j.topics||[];}catch(e){topics=[];}AI_HOT_TOPICS.forEach(ai=>{if(!topics.find(t=>(t.topic||'').toLowerCase()===ai.toLowerCase())){topics.unshift({label:'#'+ai.replace(/\s+/g,''),topic:ai,count:0});}});row.innerHTML=topics.slice(0,14).map(t=>`<button class="hot-chip" style="flex:0 0 auto;background:#222;border:1px solid #333;color:#ddd;border-radius:16px;padding:5px 10px;font-size:11px;cursor:pointer" onclick="document.getElementById('ai-topic-input-final5').value='${esc(t.topic).replace(/'/g,'\\''}';document.getElementById('ai-topic-input-final5').focus();searchTopic('${esc(t.topic).replace(/'/g,'\\''}')">${esc(t.label)}</button>`).join('');inp.insertAdjacentElement('afterend',row);}

// ===== SSE AUTO UPDATE =====
let _sseSource=null;
function connectSSE(){try{_sseSource=new EventSource('/api/events');_sseSource.onmessage=e=>{try{const d=JSON.parse(e.data);if(d.type==='new_post'||d.type==='new_short'){fetch('/api/ai_wall').then(r=>r.json()).then(j=>{_wallPosts=j.posts||[];if(typeof renderShortAISlide==='function')renderShortAISlide();const track=document.getElementById('ai-wall-track');if(track)_wallPosts.length?track.innerHTML=_wallPosts.slice(0,20).map((p,i)=>makeWallItem(p,i)).join(''):null;});}}catch{}};_sseSource.onerror=()=>setTimeout(connectSSE,5000);}catch(e){}}
let _lastWallLen=0;
setInterval(()=>{fetch('/api/ai_wall').then(r=>r.json()).then(j=>{const w=j.posts||[];if(w.length!==_lastWallLen){_lastWallLen=w.length;_wallPosts=w;const track=document.getElementById('ai-wall-track');if(track)w.length?track.innerHTML=w.slice(0,20).map((p,i)=>makeWallItem(p,i)).join(''):null;if(typeof renderShortAISlide==='function')renderShortAISlide();}}).catch(()=>{});},30000);
connectSSE();

// ===== Short AI slide =====
async function renderShortAISlide(){let home=document.getElementById('view-home');if(!home)return;document.getElementById('short-ai-final-slide')?.remove();let wall=(await fetch('/api/ai_wall').then(r=>r.json()).catch(()=>({posts:[]}))).posts||[];let vids=wall.filter(p=>p.video);if(!vids.length)return;let wrap=document.createElement('div');wrap.id='short-ai-final-slide';wrap.className='slider-wrap';wrap.innerHTML='<div class="slider-header"><span class="slider-label">🎬 Short AI</span></div><div class="slider-track">'+vids.slice(0,30).map((p,i)=>`<div class="slider-item shorts-item" onclick="openAIShortFeed(${i})"><div class="slider-thumb shorts-thumb"><video src="${p.video}" muted preload="metadata" style="width:100%;height:100%;object-fit:cover"></video><div class="card-play">▶</div></div><div class="slider-title">${esc(p.title)}</div></div>`).join('')+'</div>';let comp=home.querySelector('.ai-compose');(comp&&comp.nextSibling?comp.parentNode.insertBefore(wrap,comp.nextSibling):home.prepend(wrap));}
setTimeout(renderShortAISlide,2500);

setInterval(ensureHotTopics,2000);
})();
</script>
'''

# Register route if possible
if f6 and app:
    try:
        # Remove duplicate / route to avoid conflict
        original_routes = [r for r in app.router.routes if not (getattr(r,'path',None)=='/' and 'GET' in getattr(r,'methods',set()))]
        app.router.routes = original_routes
        
        @app.get('/')
        async def patch_homepage():
            html = f5.f4.f3.f2.f1._load_index_html() if f5 else "<html><body></body></html>"
            body = ""
            if hasattr(rt,'old') and hasattr(rt.old,'PATCH_INJECT'):
                body += getattr(rt.old,'PATCH_INJECT','')
            if f5:
                body += getattr(f5.f4.f3.f2.f1,'FINAL_INJECT','') if hasattr(f5,'f4') else ''
                body += getattr(f5.f4.f3,'FINAL3_INJECT','') if hasattr(f5,'f4') else ''
                body += getattr(f5.f4,'FINAL4_INJECT','') if hasattr(f5,'f4') else ''
                body += getattr(f5,'FINAL5_INJECT','') if hasattr(f5,'f4') else ''
            body += getattr(f6,'FINAL6_INJECT','') if f6 else ''
            body += getattr(f6,'FINAL6_FAST_HOME_INJECT','') if f6 else ''
            body += getattr(f6,'FINAL6E_INJECT','') if f6 else ''
            body += PATCH_CSS_JS
            if '</body>' in html:
                html = html.replace('</body>', body + '\n</body>')
            else:
                html = html + body
            return HTMLResponse(html)
    except Exception as e:
        print(f"[ERROR] register route: {e}")