bep40 commited on
Commit
b915c23
·
verified ·
1 Parent(s): a703232
Files changed (1) hide show
  1. ai_runtime_patch_fast.py +55 -95
ai_runtime_patch_fast.py CHANGED
@@ -176,116 +176,76 @@ def _split_into_segments(text, max_segments=10, min_len=30):
176
  # ======================================================================
177
  # FRAME CREATION — with Vietnamese font support
178
  # ======================================================================
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
179
 
180
-
181
- def _make_frame(post, seg, idx, total, img_path, downloaded, frame_path):
182
- """Create a single frame with Vietnamese text support - matching original layout."""
183
  from PIL import Image, ImageDraw
184
  _find_vn_fonts()
185
 
186
- W, H = 1080, 1920
187
- hero_h = 760
188
- bg = Image.new('RGB', (W, H), (12, 12, 12))
189
  draw = ImageDraw.Draw(bg)
190
 
191
- # Background image (hero)
192
  if downloaded:
193
  try:
194
  im = Image.open(img_path).convert('RGB')
195
- ratio = im.width / max(1, im.height)
196
- tr = W / hero_h
197
- if ratio > tr:
198
- nh = hero_h; nw = int(nh * ratio)
199
- else:
200
- nw = W; nh = int(nw / ratio)
201
- im = im.resize((nw, nh))
202
- left = (nw - W) // 2
203
- top = (nh - hero_h) // 2
204
- bg.paste(im.crop((left, top, left + W, top + hero_h)), (0, 0))
205
  except Exception as e:
206
  _log.warning(f"paste img: {e}")
207
 
208
- try:
209
- fb = ImageFont.truetype('/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf', 58)
210
- except:
211
- fb = None
212
-
213
- # Source badge on top image corner
214
- badge = 'Nguồn: ' + _source_badge_url_first(post)
215
- try:
216
- b = draw.textbbox((0, 0), badge, font=fb); bw = b[2] - b[0]; bh = b[3] - b[1]
217
- except:
218
- bw = len(badge) * 16; bh = 34
219
- bx = W - bw - 42; by = 24
220
- draw.rounded_rectangle((bx - 16, by - 8, W - 24, by + bh + 14), radius=18, fill=(0, 0, 0, 170))
221
- draw.text((bx, by), badge, fill=(255, 255, 255), font=fb)
222
-
223
- # Bottom text area
224
- draw.rectangle((0, hero_h - 20, W, H), fill=(12, 12, 12))
225
-
226
- # Progress bars centered
227
- total = max(1, total)
228
- total_w = total * 38 - 14
229
- start = (W - total_w) // 2
230
- for i in range(total):
231
- fill = (92, 184, 122) if i == idx else (70, 70, 70)
232
- draw.rounded_rectangle((start + i * 38, 820, start + i * 38 + 24, 832), radius=6, fill=fill)
233
-
234
- brand = 'VNEWS AI SHORT'
235
- try:
236
- bb = draw.textbbox((0, 0), brand, font=fb); tx = (W - (bb[2] - bb[0])) // 2
237
- except:
238
- tx = 360
239
- draw.text((tx, 870), brand, fill=(110, 231, 143), font=fb)
240
-
241
- seg_text = _strip_bullet_prefix(seg)
242
- lines = wrap_text_vn(draw, seg_text, fb, W - 120, 8) if fb else [seg_text[:80]]
243
- block_h = len(lines) * 74
244
- y = max(980, 1250 - block_h // 2)
245
- _draw_center(draw, lines, fb, y, (255, 255, 255), W, 74)
246
 
247
- title_lines = wrap_text_vn(draw, _strip_bullet_prefix(post.get('title', '')), fb, W - 120, 3) if fb else []
248
- y2 = 1640
249
- draw.line((80, y2 - 26, W - 80, y2 - 26), fill=(70, 70, 70), width=2)
250
- _draw_center(draw, title_lines, fb, y2, (220, 220, 220), W, 42)
251
 
252
- bg.save(frame_path, quality=92)
253
  return True
254
 
255
-
256
- def _source_badge_url_first(post):
257
- d = _domain(post.get('url', ''))
258
- if d:
259
- return d
260
- for s in post.get('sources') or []:
261
- d = _domain(s.get('url', ''))
262
- if d:
263
- return d
264
- return 'VNEWS'
265
-
266
-
267
- def wrap_text_vn(draw, text, font, maxw, max_lines):
268
- words = _clean(text).split()
269
- lines = []
270
- cur = ''
271
- for w in words:
272
- test = (cur + ' ' + w).strip()
273
- try:
274
- width = draw.textbbox((0, 0), test, font=font)[2]
275
- except:
276
- width = len(test) * 22
277
- if width <= maxw:
278
- cur = test
279
- else:
280
- if cur:
281
- lines.append(cur)
282
- cur = w
283
- if len(lines) >= max_lines:
284
- break
285
- if cur and len(lines) < max_lines:
286
- lines.append(cur)
287
- return lines
288
-
289
  # ======================================================================
290
  # SHORT VIDEO GENERATOR — FULLY ROBUST
291
  # ======================================================================
@@ -369,7 +329,7 @@ def _gen_short_sync(post) -> str:
369
  frame_ok = False
370
  if has_pil:
371
  try:
372
- _make_frame(post, seg, idx, len(segments), img_path, downloaded, frame_path)
373
  if os.path.exists(frame_path) and os.path.getsize(frame_path) > 1000:
374
  frame_ok = True
375
  else:
 
176
  # ======================================================================
177
  # FRAME CREATION — with Vietnamese font support
178
  # ======================================================================
179
+ def _draw_vn_text(draw, text, font, x, y, max_width, max_lines=6, fill=(255,255,255)):
180
+ """Draw text with word wrapping, handling Vietnamese characters."""
181
+ if font is None:
182
+ return y
183
+
184
+ words = text.split()
185
+ lines = []
186
+ current_line = []
187
+
188
+ for w in words:
189
+ test_line = ' '.join(current_line + [w])
190
+ try:
191
+ w_px = font.getlength(test_line)
192
+ except:
193
+ try:
194
+ w_px = draw.textbbox((0,0), test_line, font=font)[2]
195
+ except:
196
+ w_px = len(test_line) * 22
197
+
198
+ if w_px <= max_width:
199
+ current_line.append(w)
200
+ else:
201
+ if current_line:
202
+ lines.append(' '.join(current_line))
203
+ current_line = [w]
204
+ if len(lines) >= max_lines:
205
+ break
206
+
207
+ if current_line and len(lines) < max_lines:
208
+ lines.append(' '.join(current_line))
209
+
210
+ for ln in lines[:max_lines]:
211
+ draw.text((x, y), ln, fill=fill, font=font)
212
+ y += 55
213
+
214
+ return y
215
 
216
+ def _make_frame(post, seg, idx, img_path, downloaded, frame_path):
217
+ """Create a single frame with Vietnamese text support."""
 
218
  from PIL import Image, ImageDraw
219
  _find_vn_fonts()
220
 
221
+ sz = (1080, 1920)
222
+ bg = Image.new('RGB', sz, (15, 23, 38))
 
223
  draw = ImageDraw.Draw(bg)
224
 
225
+ # Background image
226
  if downloaded:
227
  try:
228
  im = Image.open(img_path).convert('RGB')
229
+ im = im.resize((1080, 760))
230
+ bg.paste(im, (0, 0))
231
+ overlay = Image.new('RGBA', (1080, 400), (0, 0, 0, 180))
232
+ bg.paste(overlay, (0, 720), overlay)
 
 
 
 
 
 
233
  except Exception as e:
234
  _log.warning(f"paste img: {e}")
235
 
236
+ # Title bar
237
+ draw.rectangle([0, 0, sz[0], 100], fill=(25, 118, 210))
238
+ ttl = post.get('title', '')[:50]
239
+ if _FONT_BOLD:
240
+ draw.text((sz[0]//2, 50), ttl, fill='white', font=_FONT_BOLD, anchor='mm')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
241
 
242
+ # Segment text
243
+ seg_text = seg[:200]
244
+ _draw_vn_text(draw, seg_text, _FONT_REGULAR, 80, 820, 920, max_lines=8)
 
245
 
246
+ bg.save(frame_path, quality=88)
247
  return True
248
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
249
  # ======================================================================
250
  # SHORT VIDEO GENERATOR — FULLY ROBUST
251
  # ======================================================================
 
329
  frame_ok = False
330
  if has_pil:
331
  try:
332
+ _make_frame(post, seg, idx, img_path, downloaded, frame_path)
333
  if os.path.exists(frame_path) and os.path.getsize(frame_path) > 1000:
334
  frame_ok = True
335
  else: