bep40 commited on
Commit
16ee98b
·
verified ·
1 Parent(s): c1c9987

FIX: ai_patch.py - Vietnamese font auto-detection, robust text segmentation, word wrapping

Browse files
Files changed (1) hide show
  1. ai_patch.py +142 -17
ai_patch.py CHANGED
@@ -1,3 +1,6 @@
 
 
 
1
  import os
2
  import re
3
  import time
@@ -22,6 +25,77 @@ except Exception:
22
 
23
  _STOP = set('và của các những một được trong với cho tại sau trước khi không người vietnam hôm nay mới nhất nóng tin tức cập nhật theo từ đến là có thì này đã để'.split())
24
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  def _clean(s):
26
  """Clean text - replace None with empty string, collapse whitespace."""
27
  s = s or ''
@@ -157,39 +231,90 @@ def _source_line(sources=None):
157
  return "Nguồn tham khảo: VNEWS"
158
 
159
  def _summary_segments_from_post(post, max_segments=15):
 
160
  text = post.get('text', '')
161
  segs = []
162
- if text:
163
- for line in text.split('\n'):
164
- line = _clean(line)
165
- if line:
166
- segs.append(line)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
167
  return segs[:max_segments]
168
 
169
  def _make_scene_frame(post, segment, idx, total, img_path, out_path, emotion='neutral'):
 
170
  from PIL import Image, ImageDraw, ImageFont
 
 
171
  sz = (1080, 1920)
172
  bg = Image.new('RGB', sz, (15, 23, 38))
173
  d = ImageDraw.Draw(bg)
174
- try:
175
- f = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 48)
176
- fb = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 56)
177
- except:
178
- f = ImageFont.load_default()
179
- fb = f
180
  # Draw title bar
181
  d.rectangle([0, 0, sz[0], 120], fill=(25, 118, 210))
182
  title = post.get('title', '')[:60]
183
  d.text((sz[0]//2, 60), title, fill='white', font=fb, anchor='mm')
184
- # Draw segment text
185
- y = 180
186
- for line in segment[:120]:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
187
  y += 56
188
- if y > sz[1] - 100:
189
  break
190
- d.text((80, y), line, fill='white', font=f)
191
  bg.save(out_path, quality=85)
192
 
193
  @app.get('/health')
194
  async def health():
195
- return JSONResponse({'status': 'ok'})
 
1
+ """ai_patch.py — Helper functions for rewrite, summary, short frame creation.
2
+ FIX v2: Vietnamese font auto-detection (not hardcoded DejaVu paths).
3
+ """
4
  import os
5
  import re
6
  import time
 
25
 
26
  _STOP = set('và của các những một được trong với cho tại sau trước khi không người vietnam hôm nay mới nhất nóng tin tức cập nhật theo từ đến là có thì này đã để'.split())
27
 
28
+ # ===== VIETNAMESE FONT DETECTION =====
29
+ _VN_FONT_REG = None
30
+ _VN_FONT_BOLD = None
31
+
32
+ def _get_vn_fonts():
33
+ """Auto-detect fonts that support Vietnamese diacritics."""
34
+ global _VN_FONT_REG, _VN_FONT_BOLD
35
+ if _VN_FONT_REG is not None:
36
+ return _VN_FONT_REG, _VN_FONT_BOLD
37
+
38
+ # Try system fonts in priority order (Noto first for full Vietnamese)
39
+ reg_paths = [
40
+ "/usr/share/fonts/truetype/noto/NotoSans-Regular.ttf",
41
+ "/usr/share/fonts/truetype/noto/NotoSans-Variable.ttf",
42
+ "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
43
+ "/usr/share/fonts/truetype/freefont/FreeSans.ttf",
44
+ "/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf",
45
+ ]
46
+ bold_paths = [
47
+ "/usr/share/fonts/truetype/noto/NotoSans-Bold.ttf",
48
+ "/usr/share/fonts/truetype/noto/NotoSans-Variable.ttf",
49
+ "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf",
50
+ "/usr/share/fonts/truetype/freefont/FreeSans.ttf",
51
+ "/usr/share/fonts/truetype/liberation/LiberationSans-Bold.ttf",
52
+ ]
53
+
54
+ # Also search broadly
55
+ try:
56
+ result = subprocess.run(
57
+ ['find', '/usr/share/fonts', '-name', '*.ttf', '-type', 'f'],
58
+ capture_output=True, text=True, timeout=5
59
+ )
60
+ all_ttf = result.stdout.strip().split('\n')
61
+ for key in ['noto', 'dejavu', 'liberation', 'freefont', 'vn']:
62
+ for path in all_ttf:
63
+ pl = path.lower()
64
+ if key in pl:
65
+ if ('bold' in pl or 'variable' in pl) and _VN_FONT_BOLD is None:
66
+ try:
67
+ _VN_FONT_BOLD = ImageFont.truetype(path, 56)
68
+ except:
69
+ pass
70
+ elif ('regular' in pl or path == '/usr/share/fonts/truetype/vn/VNFont.ttf') and _VN_FONT_REG is None:
71
+ try:
72
+ _VN_FONT_REG = ImageFont.truetype(path, 48)
73
+ except:
74
+ pass
75
+ except:
76
+ pass
77
+
78
+ # Try regular paths
79
+ for path in reg_paths:
80
+ if os.path.exists(path) and _VN_FONT_REG is None:
81
+ try:
82
+ _VN_FONT_REG = ImageFont.truetype(path, 48)
83
+ except:
84
+ continue
85
+ for path in bold_paths:
86
+ if os.path.exists(path) and _VN_FONT_BOLD is None:
87
+ try:
88
+ _VN_FONT_BOLD = ImageFont.truetype(path, 56)
89
+ except:
90
+ continue
91
+
92
+ if _VN_FONT_REG is None:
93
+ _VN_FONT_REG = ImageFont.load_default()
94
+ if _VN_FONT_BOLD is None:
95
+ _VN_FONT_BOLD = _VN_FONT_REG
96
+
97
+ return _VN_FONT_REG, _VN_FONT_BOLD
98
+
99
  def _clean(s):
100
  """Clean text - replace None with empty string, collapse whitespace."""
101
  s = s or ''
 
231
  return "Nguồn tham khảo: VNEWS"
232
 
233
  def _summary_segments_from_post(post, max_segments=15):
234
+ """Split post text into segments for short video. Uses multi-strategy like main generator."""
235
  text = post.get('text', '')
236
  segs = []
237
+ if not text:
238
+ return segs
239
+
240
+ # Strategy 1: split by newlines (bullet points)
241
+ for line in text.split('\n'):
242
+ line = _clean(line)
243
+ line_bare = re.sub(r'^[•\-\*\d\.\)\s]+', '', line).strip()
244
+ if len(line_bare) > 25:
245
+ segs.append(line_bare)
246
+
247
+ # Strategy 2: split by sentences
248
+ if len(segs) < 3:
249
+ segs = []
250
+ for sent in re.split(r'(?<=[.!?])\s+(?=[A-Z0-9À-ỸĐ])', text):
251
+ sent = _clean(sent)
252
+ if len(sent) > 25:
253
+ segs.append(sent)
254
+
255
+ # Strategy 3: chunk by size
256
+ if len(segs) < 2:
257
+ segs = []
258
+ words = text.split()
259
+ chunk = []
260
+ ccount = 0
261
+ for w in words:
262
+ chunk.append(w)
263
+ ccount += len(w) + 1
264
+ if ccount > 150:
265
+ segs.append(' '.join(chunk))
266
+ chunk = []
267
+ ccount = 0
268
+ if chunk:
269
+ segs.append(' '.join(chunk))
270
+
271
  return segs[:max_segments]
272
 
273
  def _make_scene_frame(post, segment, idx, total, img_path, out_path, emotion='neutral'):
274
+ """Create a scene frame with Vietnamese font support."""
275
  from PIL import Image, ImageDraw, ImageFont
276
+ f, fb = _get_vn_fonts()
277
+
278
  sz = (1080, 1920)
279
  bg = Image.new('RGB', sz, (15, 23, 38))
280
  d = ImageDraw.Draw(bg)
281
+
 
 
 
 
 
282
  # Draw title bar
283
  d.rectangle([0, 0, sz[0], 120], fill=(25, 118, 210))
284
  title = post.get('title', '')[:60]
285
  d.text((sz[0]//2, 60), title, fill='white', font=fb, anchor='mm')
286
+
287
+ # Draw segment text with word wrapping
288
+ max_width = 920
289
+ words = (segment or '')[:200].split()
290
+ lines = []
291
+ cur = []
292
+ for w in words:
293
+ test = ' '.join(cur + [w])
294
+ try:
295
+ tw = d.textbbox((0, 0), test, font=f)[2]
296
+ except:
297
+ tw = len(test) * 22
298
+ if tw <= max_width:
299
+ cur.append(w)
300
+ else:
301
+ if cur:
302
+ lines.append(' '.join(cur))
303
+ cur = [w]
304
+ if len(lines) >= 10:
305
+ break
306
+ if cur and len(lines) < 10:
307
+ lines.append(' '.join(cur))
308
+
309
+ y = 200
310
+ for line in lines:
311
+ d.text((80, y), line, fill='white', font=f)
312
  y += 56
313
+ if y > sz[1] - 80:
314
  break
315
+
316
  bg.save(out_path, quality=85)
317
 
318
  @app.get('/health')
319
  async def health():
320
+ return JSONResponse({'status': 'ok'})