yukee1992 commited on
Commit
4c9962f
·
verified ·
1 Parent(s): 40b0e60

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +204 -8
app.py CHANGED
@@ -11,9 +11,11 @@ from fastapi.middleware.cors import CORSMiddleware
11
  import uvicorn
12
  from huggingface_hub import HfApi, hf_hub_download
13
  from typing import Optional, List, Dict, Any
 
 
14
 
15
  print("=" * 60)
16
- print("🚀 TEXT STYLING API - ASS SUBTITLE METHOD")
17
  print("=" * 60)
18
 
19
  # =============================================
@@ -46,7 +48,7 @@ def download_all_fonts():
46
  fonts = {}
47
 
48
  for file in files:
49
- if file.endswith(('.ttf', '.otf')):
50
  font_name = os.path.basename(file)
51
  local_path = os.path.join(FONTS_DIR, font_name)
52
 
@@ -66,7 +68,7 @@ def download_all_fonts():
66
  fonts[font_key] = {
67
  "path": local_path,
68
  "name": font_name,
69
- "display_name": font_name.replace('.ttf', '').replace('.otf', '').replace('-', ' ')
70
  }
71
 
72
  print(f"✅ Loaded {len(fonts)} fonts")
@@ -78,6 +80,25 @@ def download_all_fonts():
78
  # Download fonts at startup
79
  FONTS = download_all_fonts()
80
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
  # =============================================
82
  # CREATE FASTAPI APP
83
  # =============================================
@@ -162,6 +183,7 @@ def create_text_overlay(input_video, output_video, text_style):
162
  return False
163
 
164
  print(f"✅ Using font: {font_path}")
 
165
 
166
  # Create working directory for ASS file
167
  work_dir = os.path.dirname(output_video)
@@ -171,7 +193,8 @@ def create_text_overlay(input_video, output_video, text_style):
171
  color_map = {
172
  "white": "FFFFFF", "black": "000000", "red": "FF0000",
173
  "green": "00FF00", "blue": "0000FF", "yellow": "FFFF00",
174
- "gold": "FFD700", "purple": "800080", "cyan": "00FFFF"
 
175
  }
176
 
177
  # Get font color
@@ -248,12 +271,174 @@ Dialogue: 0,0:00:00.00,0:00:10.00,Default,,0,0,0,,{text_style.text}"""
248
  return True
249
 
250
  # =============================================
251
- # API ENDPOINTS
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
252
  # =============================================
253
 
254
  @app.get("/health")
255
  async def health():
256
- return {"status": "healthy", "fonts_loaded": len(FONTS)}
 
 
 
 
 
257
 
258
  @app.get("/fonts")
259
  async def list_fonts():
@@ -278,6 +463,12 @@ async def style_video(request: StylingRequest):
278
  titled_path = os.path.join(work_dir, "titled.mp4")
279
  if create_text_overlay(current_video, titled_path, request.title_overlay):
280
  current_video = titled_path
 
 
 
 
 
 
281
 
282
  # Upload styled video
283
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
@@ -312,11 +503,16 @@ async def style_video(request: StylingRequest):
312
  async def root():
313
  return {
314
  "name": "Text Styling API",
315
- "version": "1.0.0",
316
  "endpoints": {
317
  "style": "POST /api/style",
318
  "fonts": "GET /fonts",
319
- "health": "GET /health"
 
 
 
 
 
320
  },
321
  "fonts_loaded": len(FONTS)
322
  }
 
11
  import uvicorn
12
  from huggingface_hub import HfApi, hf_hub_download
13
  from typing import Optional, List, Dict, Any
14
+ import base64
15
+ from io import BytesIO
16
 
17
  print("=" * 60)
18
+ print("🚀 TEXT STYLING API - ASS SUBTITLE METHOD WITH DEBUG")
19
  print("=" * 60)
20
 
21
  # =============================================
 
48
  fonts = {}
49
 
50
  for file in files:
51
+ if file.endswith(('.ttf', '.otf', '.ttc')):
52
  font_name = os.path.basename(file)
53
  local_path = os.path.join(FONTS_DIR, font_name)
54
 
 
68
  fonts[font_key] = {
69
  "path": local_path,
70
  "name": font_name,
71
+ "display_name": font_name.replace('.ttf', '').replace('.otf', '').replace('.ttc', '').replace('-', ' ')
72
  }
73
 
74
  print(f"✅ Loaded {len(fonts)} fonts")
 
80
  # Download fonts at startup
81
  FONTS = download_all_fonts()
82
 
83
+ # =============================================
84
+ # TRY TO IMPORT PIL (for debug endpoints)
85
+ # =============================================
86
+ try:
87
+ from PIL import Image, ImageFont, ImageDraw
88
+ PIL_AVAILABLE = True
89
+ print("✅ PIL available for font testing")
90
+ except ImportError:
91
+ PIL_AVAILABLE = False
92
+ print("⚠️ PIL not available - font testing limited")
93
+
94
+ try:
95
+ from fontTools import ttLib
96
+ FONTTOOLS_AVAILABLE = True
97
+ print("✅ fontTools available for font analysis")
98
+ except ImportError:
99
+ FONTTOOLS_AVAILABLE = False
100
+ print("⚠️ fontTools not available - font analysis limited")
101
+
102
  # =============================================
103
  # CREATE FASTAPI APP
104
  # =============================================
 
183
  return False
184
 
185
  print(f"✅ Using font: {font_path}")
186
+ print(f"📝 Text to render: {text_style.text}")
187
 
188
  # Create working directory for ASS file
189
  work_dir = os.path.dirname(output_video)
 
193
  color_map = {
194
  "white": "FFFFFF", "black": "000000", "red": "FF0000",
195
  "green": "00FF00", "blue": "0000FF", "yellow": "FFFF00",
196
+ "gold": "FFD700", "purple": "800080", "cyan": "00FFFF",
197
+ "pink": "FFC0CB", "orange": "FFA500", "brown": "A52A2A"
198
  }
199
 
200
  # Get font color
 
271
  return True
272
 
273
  # =============================================
274
+ # DEBUG ENDPOINTS
275
+ # =============================================
276
+
277
+ @app.get("/debug/characters/{font_name}")
278
+ async def debug_characters(font_name: str):
279
+ """Test if font supports specific Chinese characters"""
280
+ if not PIL_AVAILABLE:
281
+ return {"error": "PIL not installed - cannot test characters"}
282
+
283
+ try:
284
+ font_path = None
285
+ for key, font_info in FONTS.items():
286
+ if font_name in key or font_name in font_info["name"]:
287
+ font_path = font_info["path"]
288
+ break
289
+
290
+ if not font_path:
291
+ return {"error": f"Font {font_name} not found"}
292
+
293
+ # Test different character sets
294
+ test_texts = [
295
+ ("English", "ABCDEFGHIJKLMNOPQRSTUVWXYZ"),
296
+ ("Numbers", "0123456789"),
297
+ ("Common Chinese", "的一是在不了有和人这中大为上个国我以要他时来用们生到作地于出就分对成会可主发年动同工也能下过子说产种面而方后多定行学法所民得经十三之进等着部度家电力里如水化高自二理起小物现实加量都两体制机当使点从业本去把性好应开它合还因由其些然前外天政四日那社义事平形相全表间样与关各重新线内数正心反你明看原又么利比或但质气第向道命此变条只没结解问意建月公无系军很情者最立代想已通并提直题党程展五果料象员革位入常文总次品式活设及管特件长求老头基资边流路级少图山统接知较将组见计别她手角期根论运农指几九区强放决西被干做必战先回则任取据处队南给色光门即保治北造百规热领七海口东导器压志世金增争济阶油思术极交受联什认六共权收证改清己美再采转更单风切打白教速花带安场身车例真务具万每目至达走积示议声报斗完类八离华名确才科张信马节话米整空元况今集温传土许步群广石记需段研界拉林律叫且究观越织装影算低持音众书布复容儿须际商非验连断深难近矿千周委素技备半办青省列习响约支般史感劳便团往酸历市克何除消构府称太准精值号率族维划选标写存候毛亲快效斯院查江型眼王按格养易置派层片始却专状育厂京识适属圆包火住调满县局照参红细引听该铁价严龙飞"),
298
+ ("Test Phrase", "荆南麦圆体测试"),
299
+ ("Your Text", font_name)
300
+ ]
301
+
302
+ images = []
303
+ for label, text in test_texts:
304
+ # Create image
305
+ img = Image.new('RGB', (1200, 400), color='white')
306
+ d = ImageDraw.Draw(img)
307
+
308
+ try:
309
+ # Try to load font with size 24
310
+ font = ImageFont.truetype(font_path, 24)
311
+ # Draw label
312
+ d.text((10, 10), f"{label}:", fill='black', font=font)
313
+ # Draw text (wrap if too long)
314
+ d.text((10, 50), str(text)[:200], fill='black', font=font)
315
+ except Exception as e:
316
+ d.text((10, 10), f"Error: {str(e)}", fill='red', font=ImageFont.load_default())
317
+
318
+ # Convert to base64
319
+ buffered = BytesIO()
320
+ img.save(buffered, format="PNG")
321
+ img_base64 = base64.b64encode(buffered.getvalue()).decode()
322
+ images.append(img_base64)
323
+
324
+ return {
325
+ "font_name": font_name,
326
+ "font_path": font_path,
327
+ "font_size_bytes": os.path.getsize(font_path),
328
+ "test_images": images,
329
+ "message": "If Chinese characters appear as boxes, the font lacks those glyphs"
330
+ }
331
+
332
+ except Exception as e:
333
+ return {"error": str(e)}
334
+
335
+ @app.get("/debug/font-info/{font_name}")
336
+ async def font_info(font_name: str):
337
+ """Get detailed font information"""
338
+ try:
339
+ font_path = None
340
+ for key, font_info in FONTS.items():
341
+ if font_name in key or font_name in font_info["name"]:
342
+ font_path = font_info["path"]
343
+ break
344
+
345
+ if not font_path:
346
+ return {"error": f"Font {font_name} not found"}
347
+
348
+ info = {
349
+ "font_name": font_name,
350
+ "path": font_path,
351
+ "size_bytes": os.path.getsize(font_path),
352
+ "pil_available": PIL_AVAILABLE,
353
+ "fonttools_available": FONTTOOLS_AVAILABLE
354
+ }
355
+
356
+ # Try to get basic info with PIL
357
+ if PIL_AVAILABLE:
358
+ try:
359
+ font = ImageFont.truetype(font_path, 20)
360
+ info["pil_loads"] = True
361
+ except Exception as e:
362
+ info["pil_loads"] = False
363
+ info["pil_error"] = str(e)
364
+
365
+ # Try to get detailed info with fontTools
366
+ if FONTTOOLS_AVAILABLE:
367
+ try:
368
+ from fontTools import ttLib
369
+ font = ttLib.TTFont(font_path)
370
+
371
+ # Get font name
372
+ name_records = {}
373
+ for record in font['name'].names:
374
+ try:
375
+ if record.nameID == 1:
376
+ name_records['family'] = record.string.decode('utf-16-be')
377
+ elif record.nameID == 2:
378
+ name_records['subfamily'] = record.string.decode('utf-16-be')
379
+ elif record.nameID == 4:
380
+ name_records['full'] = record.string.decode('utf-16-be')
381
+ elif record.nameID == 6:
382
+ name_records['postscript'] = record.string.decode('utf-16-be')
383
+ except:
384
+ pass
385
+
386
+ info["names"] = name_records
387
+
388
+ # Check for Chinese glyphs
389
+ cmap = font.getBestCmap()
390
+ chinese_ranges = [
391
+ (0x4E00, 0x9FFF), # CJK Unified Ideographs
392
+ (0x3400, 0x4DBF), # Extension A
393
+ (0x20000, 0x2A6DF), # Extension B
394
+ ]
395
+
396
+ has_chinese = False
397
+ chinese_count = 0
398
+ for start, end in chinese_ranges:
399
+ for code in range(start, min(start+1000, end), 100):
400
+ if code in cmap:
401
+ has_chinese = True
402
+ chinese_count += 1
403
+
404
+ info["has_chinese_glyphs"] = has_chinese
405
+ info["approx_chinese_glyphs"] = chinese_count * 100
406
+ info["total_glyphs"] = len(font.getGlyphOrder())
407
+
408
+ except Exception as e:
409
+ info["fonttools_error"] = str(e)
410
+
411
+ return info
412
+
413
+ except Exception as e:
414
+ return {"error": str(e)}
415
+
416
+ @app.get("/debug/font-list")
417
+ async def debug_font_list():
418
+ """List all fonts with basic info"""
419
+ font_list = []
420
+ for key, font_info in FONTS.items():
421
+ font_list.append({
422
+ "id": key,
423
+ "name": font_info["display_name"],
424
+ "file": font_info["name"],
425
+ "path": font_info["path"],
426
+ "size_kb": round(os.path.getsize(font_info["path"]) / 1024, 1)
427
+ })
428
+ return {"fonts": font_list}
429
+
430
+ # =============================================
431
+ # MAIN API ENDPOINTS
432
  # =============================================
433
 
434
  @app.get("/health")
435
  async def health():
436
+ return {
437
+ "status": "healthy",
438
+ "fonts_loaded": len(FONTS),
439
+ "pil_available": PIL_AVAILABLE,
440
+ "fonttools_available": FONTTOOLS_AVAILABLE
441
+ }
442
 
443
  @app.get("/fonts")
444
  async def list_fonts():
 
463
  titled_path = os.path.join(work_dir, "titled.mp4")
464
  if create_text_overlay(current_video, titled_path, request.title_overlay):
465
  current_video = titled_path
466
+ else:
467
+ return StylingResponse(
468
+ status="error",
469
+ project_id=request.project_id,
470
+ error="Failed to add text overlay"
471
+ )
472
 
473
  # Upload styled video
474
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
 
503
  async def root():
504
  return {
505
  "name": "Text Styling API",
506
+ "version": "2.0.0",
507
  "endpoints": {
508
  "style": "POST /api/style",
509
  "fonts": "GET /fonts",
510
+ "health": "GET /health",
511
+ "debug": {
512
+ "font_list": "GET /debug/font-list",
513
+ "font_info": "GET /debug/font-info/{font_name}",
514
+ "characters": "GET /debug/characters/{font_name}"
515
+ }
516
  },
517
  "fonts_loaded": len(FONTS)
518
  }