bep40 commited on
Commit
eee056a
·
verified ·
1 Parent(s): cd81527

Use restored full UI if local static index is placeholder

Browse files
Files changed (1) hide show
  1. ai_runtime_final.py +29 -36
ai_runtime_final.py CHANGED
@@ -1,5 +1,5 @@
1
  """Final runtime overrides for VNEWS AI UI and short-video rendering."""
2
- import os, re
3
  import ai_runtime as rt
4
  from ai_runtime import app
5
  from fastapi.responses import HTMLResponse
@@ -8,6 +8,8 @@ try:
8
  except Exception:
9
  Image = ImageDraw = ImageFont = None
10
 
 
 
11
 
12
  def clean(s):
13
  import html as html_lib
@@ -30,16 +32,9 @@ def _draw_center(draw, lines, font, y, fill, W, line_h):
30
 
31
 
32
  def final_make_frame(post,seg,idx,total,img_path,out_path):
33
- """Frame layout requested by user:
34
- - image at top
35
- - source badge at image corner
36
- - text centered in lower half
37
- - no bullet/dot prefix in scene text
38
- """
39
  if Image is None:
40
  return rt.make_frame(post,seg,idx,total,img_path,out_path)
41
- W,H=1080,1920
42
- hero_h=760
43
  bg=Image.new('RGB',(W,H),(12,12,12))
44
  try:
45
  im=Image.open(img_path).convert('RGB')
@@ -60,7 +55,6 @@ def final_make_frame(post,seg,idx,total,img_path,out_path):
60
  fsmall=ImageFont.truetype('/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf',28)
61
  except Exception:
62
  fb=ft=fs=fsmall=None
63
- # source badge, black rounded rectangle, no RGBA tuple to avoid Pillow RGB errors
64
  badge='Nguồn: '+rt._source_badge(post)
65
  try:
66
  b=draw.textbbox((0,0),badge,font=fsmall);bw=b[2]-b[0];bh=b[3]-b[1]
@@ -69,55 +63,38 @@ def final_make_frame(post,seg,idx,total,img_path,out_path):
69
  bx=W-bw-42;by=24
70
  draw.rounded_rectangle((bx-16,by-8,W-24,by+bh+14),radius=18,fill=(0,0,0))
71
  draw.text((bx,by),badge,fill=(255,255,255),font=fsmall)
72
-
73
  draw.rectangle((0,hero_h-20,W,H),fill=(12,12,12))
74
- total=max(1,total)
75
- total_w=total*38-14;start=(W-total_w)//2
76
  for i in range(total):
77
  fill=(92,184,122) if i==idx else (70,70,70)
78
  draw.rounded_rectangle((start+i*38,820,start+i*38+24,832),radius=6,fill=fill)
79
  brand='VNEWS AI SHORT'
80
  try:
81
  bb=draw.textbbox((0,0),brand,font=ft);tx=(W-(bb[2]-bb[0]))//2
82
- except Exception:
83
- tx=360
84
  draw.text((tx,870),brand,fill=(110,231,143),font=ft)
85
-
86
  seg=_strip_bullet_prefix(seg)
87
  lines=rt.wrap_text(draw,seg,fb,W-120,8)
88
- block_h=len(lines)*74
89
- y=max(980,1250-block_h//2)
90
  _draw_center(draw,lines,fb,y,(255,255,255),W,74)
91
-
92
  title_lines=rt.wrap_text(draw,_strip_bullet_prefix(post.get('title','')),fs,W-120,3)
93
  y2=1640
94
  draw.line((80,y2-26,W-80,y2-26),fill=(70,70,70),width=2)
95
  _draw_center(draw,title_lines,fs,y2,(220,220,220),W,42)
96
  bg.save(out_path,quality=92)
97
 
98
-
99
- # Monkey-patch function used by ai_runtime.short_segments
100
  rt.make_frame = final_make_frame
101
 
102
- # Replace final index route to guarantee UI changes after all previous injections.
103
  app.router.routes=[r for r in app.router.routes if not (getattr(r,'path',None)=='/' and 'GET' in getattr(r,'methods',set()))]
104
 
105
  FINAL_INJECT = r'''
106
  <style>
107
- /* URL-only AI compose: remove topic controls completely */
108
- #ai-topic-input{display:none!important}
109
- button[onclick*="createTopicPost"],*[onclick*="createTopicPost"]{display:none!important}
110
- .ai-topic-row,.topic-row,.ai-compose-topic{display:none!important}
111
- .ai-url-only-note{font-size:11px;color:#888;margin:5px 0 8px}.ai-wall-gallery{display:grid;grid-template-columns:repeat(2,1fr);gap:6px;margin:10px 0}.ai-wall-gallery img{width:100%;aspect-ratio:16/9;object-fit:cover;border-radius:8px;background:#222}.ai-wall-gallery img:first-child{grid-column:1/-1}
112
  </style>
113
  <script>
114
  (function(){
115
  function esc(s){return String(s||'').replace(/[&<>"']/g,m=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[m]));}
116
- function hideTopicControls(){
117
- document.querySelectorAll('#ai-topic-input').forEach(e=>{let row=e.closest('.ai-compose-topic,.topic-row,.ai-topic-row')||e.parentElement;if(row&&!row.querySelector('#ai-url-input'))row.style.display='none';else e.style.display='none';});
118
- document.querySelectorAll('button,a').forEach(b=>{let t=(b.textContent||'').toLowerCase();let oc=b.getAttribute('onclick')||'';if(oc.includes('createTopicPost')||t.includes('chủ đề'))b.style.display='none';});
119
- let url=document.getElementById('ai-url-input');if(url&&!document.getElementById('ai-url-only-note')){let n=document.createElement('div');n.id='ai-url-only-note';n.className='ai-url-only-note';n.textContent='Dán URL bài viết để AI tóm tắt và lấy tất cả ảnh trong bài.';url.insertAdjacentElement('afterend',n);}
120
- }
121
  window.createTopicPost=function(){alert('Đã tắt ô nhập chủ đề. Vui lòng dán URL bài viết.');};
122
  window.createUrlPost=function(){let inp=document.getElementById('ai-url-input');let url=(inp&&inp.value||'').trim();if(!url)return alert('Dán URL trước');if(!/^https?:\/\//i.test(url))return alert('URL cần bắt đầu bằng http:// hoặc https://');fetch('/api/url_wall',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({url})}).then(r=>r.json().then(j=>({ok:r.ok,j}))).then(({ok,j})=>{if(ok&&j.post){if(inp)inp.value='';alert('Đã tóm tắt URL, lấy ảnh trong bài và đăng lên Tường AI');location.reload();}else alert(j.error||'Lỗi URL')}).catch(e=>alert(e.message||'Lỗi URL'));};
123
  function galleryHtml(p){let imgs=(p.images||[]).filter(Boolean);if(!imgs.length&&p.img)imgs=[p.img];if(!imgs.length)return '';return '<div class="ai-wall-gallery">'+imgs.slice(0,12).map(u=>`<img src="${esc(u)}" loading="lazy">`).join('')+'</div>';}
@@ -128,10 +105,26 @@ setTimeout(hideTopicControls,200);setInterval(hideTopicControls,1000);
128
  </script>
129
  '''
130
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
  @app.get('/')
132
  async def index_final():
133
- with open('/app/static/index.html','r',encoding='utf-8') as f:
134
- html=f.read()
135
- old_inject=getattr(rt.old,'PATCH_INJECT','')
136
- body=old_inject + getattr(rt,'FINAL_PATCH','') + FINAL_INJECT
137
  return HTMLResponse(html.replace('</body>',body+'\n</body>') if '</body>' in html else html+body)
 
1
  """Final runtime overrides for VNEWS AI UI and short-video rendering."""
2
+ import os, re, requests
3
  import ai_runtime as rt
4
  from ai_runtime import app
5
  from fastapi.responses import HTMLResponse
 
8
  except Exception:
9
  Image = ImageDraw = ImageFont = None
10
 
11
+ RESTORE_INDEX_URL = "https://huggingface.co/spaces/bep40/vnews/raw/restore-33c3dda/static/index.html"
12
+
13
 
14
  def clean(s):
15
  import html as html_lib
 
32
 
33
 
34
  def final_make_frame(post,seg,idx,total,img_path,out_path):
 
 
 
 
 
 
35
  if Image is None:
36
  return rt.make_frame(post,seg,idx,total,img_path,out_path)
37
+ W,H=1080,1920;hero_h=760
 
38
  bg=Image.new('RGB',(W,H),(12,12,12))
39
  try:
40
  im=Image.open(img_path).convert('RGB')
 
55
  fsmall=ImageFont.truetype('/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf',28)
56
  except Exception:
57
  fb=ft=fs=fsmall=None
 
58
  badge='Nguồn: '+rt._source_badge(post)
59
  try:
60
  b=draw.textbbox((0,0),badge,font=fsmall);bw=b[2]-b[0];bh=b[3]-b[1]
 
63
  bx=W-bw-42;by=24
64
  draw.rounded_rectangle((bx-16,by-8,W-24,by+bh+14),radius=18,fill=(0,0,0))
65
  draw.text((bx,by),badge,fill=(255,255,255),font=fsmall)
 
66
  draw.rectangle((0,hero_h-20,W,H),fill=(12,12,12))
67
+ total=max(1,total);total_w=total*38-14;start=(W-total_w)//2
 
68
  for i in range(total):
69
  fill=(92,184,122) if i==idx else (70,70,70)
70
  draw.rounded_rectangle((start+i*38,820,start+i*38+24,832),radius=6,fill=fill)
71
  brand='VNEWS AI SHORT'
72
  try:
73
  bb=draw.textbbox((0,0),brand,font=ft);tx=(W-(bb[2]-bb[0]))//2
74
+ except Exception:tx=360
 
75
  draw.text((tx,870),brand,fill=(110,231,143),font=ft)
 
76
  seg=_strip_bullet_prefix(seg)
77
  lines=rt.wrap_text(draw,seg,fb,W-120,8)
78
+ block_h=len(lines)*74;y=max(980,1250-block_h//2)
 
79
  _draw_center(draw,lines,fb,y,(255,255,255),W,74)
 
80
  title_lines=rt.wrap_text(draw,_strip_bullet_prefix(post.get('title','')),fs,W-120,3)
81
  y2=1640
82
  draw.line((80,y2-26,W-80,y2-26),fill=(70,70,70),width=2)
83
  _draw_center(draw,title_lines,fs,y2,(220,220,220),W,42)
84
  bg.save(out_path,quality=92)
85
 
 
 
86
  rt.make_frame = final_make_frame
87
 
 
88
  app.router.routes=[r for r in app.router.routes if not (getattr(r,'path',None)=='/' and 'GET' in getattr(r,'methods',set()))]
89
 
90
  FINAL_INJECT = r'''
91
  <style>
92
+ #ai-topic-input{display:none!important}button[onclick*="createTopicPost"],*[onclick*="createTopicPost"]{display:none!important}.ai-topic-row,.topic-row,.ai-compose-topic{display:none!important}.ai-url-only-note{font-size:11px;color:#888;margin:5px 0 8px}.ai-wall-gallery{display:grid;grid-template-columns:repeat(2,1fr);gap:6px;margin:10px 0}.ai-wall-gallery img{width:100%;aspect-ratio:16/9;object-fit:cover;border-radius:8px;background:#222}.ai-wall-gallery img:first-child{grid-column:1/-1}
 
 
 
 
93
  </style>
94
  <script>
95
  (function(){
96
  function esc(s){return String(s||'').replace(/[&<>"']/g,m=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[m]));}
97
+ function hideTopicControls(){document.querySelectorAll('#ai-topic-input').forEach(e=>{let row=e.closest('.ai-compose-topic,.topic-row,.ai-topic-row')||e.parentElement;if(row&&!row.querySelector('#ai-url-input'))row.style.display='none';else e.style.display='none';});document.querySelectorAll('button,a').forEach(b=>{let t=(b.textContent||'').toLowerCase();let oc=b.getAttribute('onclick')||'';if(oc.includes('createTopicPost')||t.includes('chủ đề'))b.style.display='none';});let url=document.getElementById('ai-url-input');if(url&&!document.getElementById('ai-url-only-note')){let n=document.createElement('div');n.id='ai-url-only-note';n.className='ai-url-only-note';n.textContent='Dán URL bài viết để AI tóm tắt và lấy tất cả ảnh trong bài.';url.insertAdjacentElement('afterend',n);}}
 
 
 
 
98
  window.createTopicPost=function(){alert('Đã tắt ô nhập chủ đề. Vui lòng dán URL bài viết.');};
99
  window.createUrlPost=function(){let inp=document.getElementById('ai-url-input');let url=(inp&&inp.value||'').trim();if(!url)return alert('Dán URL trước');if(!/^https?:\/\//i.test(url))return alert('URL cần bắt đầu bằng http:// hoặc https://');fetch('/api/url_wall',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({url})}).then(r=>r.json().then(j=>({ok:r.ok,j}))).then(({ok,j})=>{if(ok&&j.post){if(inp)inp.value='';alert('Đã tóm tắt URL, lấy ảnh trong bài và đăng lên Tường AI');location.reload();}else alert(j.error||'Lỗi URL')}).catch(e=>alert(e.message||'Lỗi URL'));};
100
  function galleryHtml(p){let imgs=(p.images||[]).filter(Boolean);if(!imgs.length&&p.img)imgs=[p.img];if(!imgs.length)return '';return '<div class="ai-wall-gallery">'+imgs.slice(0,12).map(u=>`<img src="${esc(u)}" loading="lazy">`).join('')+'</div>';}
 
105
  </script>
106
  '''
107
 
108
+ def _load_index_html():
109
+ try:
110
+ with open('/app/static/index.html','r',encoding='utf-8') as f:
111
+ html=f.read()
112
+ except Exception:
113
+ html=''
114
+ # main currently has a placeholder in some deployments; restore full UI from branch if needed
115
+ if '<!DOCTYPE html>' not in html or '<div id="view-home"' not in html:
116
+ try:
117
+ r=requests.get(RESTORE_INDEX_URL,timeout=20)
118
+ if r.status_code==200 and '<!DOCTYPE html>' in r.text:
119
+ html=r.text
120
+ except Exception:
121
+ pass
122
+ if '<!DOCTYPE html>' not in html:
123
+ html='<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>VNEWS</title></head><body><div id="view-home">VNEWS</div></body></html>'
124
+ return html
125
+
126
  @app.get('/')
127
  async def index_final():
128
+ html=_load_index_html()
129
+ body=getattr(rt.old,'PATCH_INJECT','') + FINAL_INJECT
 
 
130
  return HTMLResponse(html.replace('</body>',body+'\n</body>') if '</body>' in html else html+body)