Spaces:
Paused
Paused
Update app.py
Browse files
app.py
CHANGED
|
@@ -135,6 +135,46 @@ except ImportError:
|
|
| 135 |
FONTTOOLS_AVAILABLE = False
|
| 136 |
print("β οΈ fontTools not available - font analysis limited")
|
| 137 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 138 |
# =============================================
|
| 139 |
# CREATE FASTAPI APP
|
| 140 |
# =============================================
|
|
@@ -425,30 +465,23 @@ def wrap_text_for_ass(text: str, max_width: int, font_family: str, font_size: in
|
|
| 425 |
return "\\N".join(lines)
|
| 426 |
|
| 427 |
def create_caption_ass(captions: List[CaptionSegment], style: CaptionStyle, work_dir: str, font_path: str) -> str:
|
| 428 |
-
"""Create ASS subtitle file for captions"""
|
| 429 |
|
| 430 |
# Get actual font family name
|
| 431 |
font_family_name = get_font_family_name(font_path)
|
| 432 |
print(f"π Caption font family: {font_family_name}")
|
| 433 |
|
| 434 |
-
# Color mapping
|
| 435 |
-
color_map = {
|
| 436 |
-
"white": "FFFFFF", "black": "000000", "red": "FF0000",
|
| 437 |
-
"green": "00FF00", "blue": "0000FF", "yellow": "FFFF00",
|
| 438 |
-
"gold": "FFD700", "purple": "800080", "cyan": "00FFFF"
|
| 439 |
-
}
|
| 440 |
-
|
| 441 |
# Get font color and convert from RRGGBB to BBGGRR for ASS
|
| 442 |
-
font_color_rgb =
|
| 443 |
# Convert RGB to BGR (swap first two and last two characters)
|
| 444 |
font_color_bgr = font_color_rgb[4:6] + font_color_rgb[2:4] + font_color_rgb[0:2]
|
| 445 |
print(f"π¨ Font color: {style.color} -> RGB={font_color_rgb} -> BGR={font_color_bgr}")
|
| 446 |
|
| 447 |
# Parse background color
|
| 448 |
bg_parts = style.bg_color.split('@')
|
| 449 |
-
bg_color_name = bg_parts[0]
|
| 450 |
bg_opacity = float(bg_parts[1]) if len(bg_parts) > 1 else 0.5
|
| 451 |
-
bg_color_rgb =
|
| 452 |
# Convert RGB to BGR for background
|
| 453 |
bg_color_bgr = bg_color_rgb[4:6] + bg_color_rgb[2:4] + bg_color_rgb[0:2]
|
| 454 |
bg_alpha = int((1 - bg_opacity) * 255)
|
|
@@ -468,8 +501,7 @@ def create_caption_ass(captions: List[CaptionSegment], style: CaptionStyle, work
|
|
| 468 |
margin_v = style.margin
|
| 469 |
|
| 470 |
# Create ASS header with proper border settings
|
| 471 |
-
# BorderStyle=
|
| 472 |
-
# We set BackColour to the background color, and it will be used as box fill
|
| 473 |
ass_header = f"""[Script Info]
|
| 474 |
; Script generated by Video Styling Space - Auto Caption
|
| 475 |
ScriptType: v4.00+
|
|
@@ -510,7 +542,7 @@ Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
|
|
| 510 |
return ass_file
|
| 511 |
|
| 512 |
# =============================================
|
| 513 |
-
# SRT PARSING FUNCTIONS
|
| 514 |
# =============================================
|
| 515 |
|
| 516 |
def parse_srt_file(srt_content: str) -> List[CaptionSegment]:
|
|
@@ -570,11 +602,11 @@ def srt_time_to_seconds(time_str: str) -> float:
|
|
| 570 |
return 0
|
| 571 |
|
| 572 |
# =============================================
|
| 573 |
-
# TEXT OVERLAY FUNCTION
|
| 574 |
# =============================================
|
| 575 |
|
| 576 |
def create_text_overlay(input_video, output_video, text_style):
|
| 577 |
-
"""Add text overlay using ASS subtitles"""
|
| 578 |
font_path = get_font_path(text_style.font_family)
|
| 579 |
if not font_path:
|
| 580 |
print(f"β οΈ Font not found: {text_style.font_family}")
|
|
@@ -590,24 +622,17 @@ def create_text_overlay(input_video, output_video, text_style):
|
|
| 590 |
work_dir = os.path.dirname(output_video)
|
| 591 |
ass_file = os.path.join(work_dir, "subtitle.ass")
|
| 592 |
|
| 593 |
-
# Color mapping
|
| 594 |
-
color_map = {
|
| 595 |
-
"white": "FFFFFF", "black": "000000", "red": "FF0000",
|
| 596 |
-
"green": "00FF00", "blue": "0000FF", "yellow": "FFFF00",
|
| 597 |
-
"gold": "FFD700", "purple": "800080", "cyan": "00FFFF"
|
| 598 |
-
}
|
| 599 |
-
|
| 600 |
# Get font color and convert from RRGGBB to BBGGRR for ASS
|
| 601 |
-
font_color_rgb =
|
| 602 |
# Convert RGB to BGR (swap first two and last two characters)
|
| 603 |
font_color_bgr = font_color_rgb[4:6] + font_color_rgb[2:4] + font_color_rgb[0:2]
|
| 604 |
print(f"π¨ Font color: {text_style.color} -> RGB={font_color_rgb} -> BGR={font_color_bgr}")
|
| 605 |
|
| 606 |
# Parse background color
|
| 607 |
bg_parts = text_style.bg_color.split('@')
|
| 608 |
-
bg_color_name = bg_parts[0]
|
| 609 |
bg_opacity = float(bg_parts[1]) if len(bg_parts) > 1 else 0.5
|
| 610 |
-
bg_color_rgb =
|
| 611 |
# Convert RGB to BGR for background
|
| 612 |
bg_color_bgr = bg_color_rgb[4:6] + bg_color_rgb[2:4] + bg_color_rgb[0:2]
|
| 613 |
bg_alpha = int((1 - bg_opacity) * 255)
|
|
@@ -712,7 +737,6 @@ Dialogue: 0,0:00:00.00,0:00:10.00,Default,,0,0,0,,{text_style.text}"""
|
|
| 712 |
print(f"β
Video styled successfully with ASS subtitles")
|
| 713 |
return True
|
| 714 |
|
| 715 |
-
|
| 716 |
# =============================================
|
| 717 |
# DEBUG ENDPOINTS
|
| 718 |
# =============================================
|
|
@@ -1001,6 +1025,19 @@ async def test_srt_parsing(project_id: str, srt_filename: str):
|
|
| 1001 |
except Exception as e:
|
| 1002 |
return {"error": str(e)}
|
| 1003 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1004 |
# =============================================
|
| 1005 |
# MAIN API ENDPOINTS
|
| 1006 |
# =============================================
|
|
@@ -1010,6 +1047,7 @@ async def health():
|
|
| 1010 |
return {
|
| 1011 |
"status": "healthy",
|
| 1012 |
"fonts_loaded": len(FONTS),
|
|
|
|
| 1013 |
"pil_available": PIL_AVAILABLE,
|
| 1014 |
"fonttools_available": FONTTOOLS_AVAILABLE,
|
| 1015 |
"whisper_available": WHISPER_AVAILABLE
|
|
@@ -1471,12 +1509,13 @@ async def add_captions_from_srt(request: SrtCaptionRequest):
|
|
| 1471 |
async def root():
|
| 1472 |
return {
|
| 1473 |
"name": "Text Styling API with Auto Caption",
|
| 1474 |
-
"version": "3.
|
| 1475 |
"features": {
|
| 1476 |
"title_overlay": "β
",
|
| 1477 |
"manual_captions": "β
",
|
| 1478 |
"auto_transcription": "β
" if WHISPER_AVAILABLE else "β",
|
| 1479 |
-
"srt_support": "β
"
|
|
|
|
| 1480 |
},
|
| 1481 |
"endpoints": {
|
| 1482 |
"style": "POST /api/style - Add title overlay",
|
|
@@ -1484,6 +1523,7 @@ async def root():
|
|
| 1484 |
"caption_from_srt": "POST /api/caption-from-srt - Add captions from SRT URL",
|
| 1485 |
"transcribe": "POST /api/transcribe-and-caption - Auto-transcribe from audio",
|
| 1486 |
"fonts": "GET /fonts - List available fonts",
|
|
|
|
| 1487 |
"health": "GET /health - Check status"
|
| 1488 |
},
|
| 1489 |
"debug": {
|
|
@@ -1491,9 +1531,11 @@ async def root():
|
|
| 1491 |
"font_info": "GET /debug/font-info/{font_name}",
|
| 1492 |
"characters": "GET /debug/characters/{font_name}",
|
| 1493 |
"test_transcription": "POST /debug/test-transcription?audio_url=...&language=zh",
|
| 1494 |
-
"test_srt": "POST /debug/test-srt - Test SRT parsing"
|
|
|
|
| 1495 |
},
|
| 1496 |
-
"fonts_loaded": len(FONTS)
|
|
|
|
| 1497 |
}
|
| 1498 |
|
| 1499 |
# =============================================
|
|
|
|
| 135 |
FONTTOOLS_AVAILABLE = False
|
| 136 |
print("β οΈ fontTools not available - font analysis limited")
|
| 137 |
|
| 138 |
+
# =============================================
|
| 139 |
+
# ENHANCED COLOR MAP
|
| 140 |
+
# =============================================
|
| 141 |
+
COLOR_MAP = {
|
| 142 |
+
# Basic colors
|
| 143 |
+
"white": "FFFFFF",
|
| 144 |
+
"black": "000000",
|
| 145 |
+
"red": "FF0000",
|
| 146 |
+
"green": "00FF00",
|
| 147 |
+
"blue": "0000FF",
|
| 148 |
+
"yellow": "FFFF00",
|
| 149 |
+
|
| 150 |
+
# Purple variations
|
| 151 |
+
"purple": "800080", # Medium purple
|
| 152 |
+
"darkpurple": "4B0082", # Indigo
|
| 153 |
+
"lightpurple": "9370DB", # Light purple
|
| 154 |
+
"violet": "EE82EE", # Violet
|
| 155 |
+
"magenta": "FF00FF", # Magenta
|
| 156 |
+
|
| 157 |
+
# Gold variations
|
| 158 |
+
"gold": "FFD700", # Gold
|
| 159 |
+
"orange": "FFA500", # Orange
|
| 160 |
+
"darkgold": "B8860B", # Dark goldenrod
|
| 161 |
+
|
| 162 |
+
# Other colors
|
| 163 |
+
"cyan": "00FFFF",
|
| 164 |
+
"pink": "FFC0CB",
|
| 165 |
+
"brown": "A52A2A",
|
| 166 |
+
"gray": "808080",
|
| 167 |
+
"navy": "000080",
|
| 168 |
+
"teal": "008080",
|
| 169 |
+
"maroon": "800000",
|
| 170 |
+
"olive": "808000",
|
| 171 |
+
"coral": "FF7F50",
|
| 172 |
+
"lavender": "E6E6FA",
|
| 173 |
+
"turquoise": "40E0D0",
|
| 174 |
+
"indigo": "4B0082",
|
| 175 |
+
"crimson": "DC143C"
|
| 176 |
+
}
|
| 177 |
+
|
| 178 |
# =============================================
|
| 179 |
# CREATE FASTAPI APP
|
| 180 |
# =============================================
|
|
|
|
| 465 |
return "\\N".join(lines)
|
| 466 |
|
| 467 |
def create_caption_ass(captions: List[CaptionSegment], style: CaptionStyle, work_dir: str, font_path: str) -> str:
|
| 468 |
+
"""Create ASS subtitle file for captions with enhanced colors"""
|
| 469 |
|
| 470 |
# Get actual font family name
|
| 471 |
font_family_name = get_font_family_name(font_path)
|
| 472 |
print(f"π Caption font family: {font_family_name}")
|
| 473 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 474 |
# Get font color and convert from RRGGBB to BBGGRR for ASS
|
| 475 |
+
font_color_rgb = COLOR_MAP.get(style.color.lower(), "FFFFFF")
|
| 476 |
# Convert RGB to BGR (swap first two and last two characters)
|
| 477 |
font_color_bgr = font_color_rgb[4:6] + font_color_rgb[2:4] + font_color_rgb[0:2]
|
| 478 |
print(f"π¨ Font color: {style.color} -> RGB={font_color_rgb} -> BGR={font_color_bgr}")
|
| 479 |
|
| 480 |
# Parse background color
|
| 481 |
bg_parts = style.bg_color.split('@')
|
| 482 |
+
bg_color_name = bg_parts[0].lower()
|
| 483 |
bg_opacity = float(bg_parts[1]) if len(bg_parts) > 1 else 0.5
|
| 484 |
+
bg_color_rgb = COLOR_MAP.get(bg_color_name, "000000")
|
| 485 |
# Convert RGB to BGR for background
|
| 486 |
bg_color_bgr = bg_color_rgb[4:6] + bg_color_rgb[2:4] + bg_color_rgb[0:2]
|
| 487 |
bg_alpha = int((1 - bg_opacity) * 255)
|
|
|
|
| 501 |
margin_v = style.margin
|
| 502 |
|
| 503 |
# Create ASS header with proper border settings
|
| 504 |
+
# BorderStyle=3 means opaque box, Outline controls box padding
|
|
|
|
| 505 |
ass_header = f"""[Script Info]
|
| 506 |
; Script generated by Video Styling Space - Auto Caption
|
| 507 |
ScriptType: v4.00+
|
|
|
|
| 542 |
return ass_file
|
| 543 |
|
| 544 |
# =============================================
|
| 545 |
+
# SRT PARSING FUNCTIONS
|
| 546 |
# =============================================
|
| 547 |
|
| 548 |
def parse_srt_file(srt_content: str) -> List[CaptionSegment]:
|
|
|
|
| 602 |
return 0
|
| 603 |
|
| 604 |
# =============================================
|
| 605 |
+
# TEXT OVERLAY FUNCTION
|
| 606 |
# =============================================
|
| 607 |
|
| 608 |
def create_text_overlay(input_video, output_video, text_style):
|
| 609 |
+
"""Add text overlay using ASS subtitles with enhanced colors"""
|
| 610 |
font_path = get_font_path(text_style.font_family)
|
| 611 |
if not font_path:
|
| 612 |
print(f"β οΈ Font not found: {text_style.font_family}")
|
|
|
|
| 622 |
work_dir = os.path.dirname(output_video)
|
| 623 |
ass_file = os.path.join(work_dir, "subtitle.ass")
|
| 624 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 625 |
# Get font color and convert from RRGGBB to BBGGRR for ASS
|
| 626 |
+
font_color_rgb = COLOR_MAP.get(text_style.color.lower(), "FFFFFF")
|
| 627 |
# Convert RGB to BGR (swap first two and last two characters)
|
| 628 |
font_color_bgr = font_color_rgb[4:6] + font_color_rgb[2:4] + font_color_rgb[0:2]
|
| 629 |
print(f"π¨ Font color: {text_style.color} -> RGB={font_color_rgb} -> BGR={font_color_bgr}")
|
| 630 |
|
| 631 |
# Parse background color
|
| 632 |
bg_parts = text_style.bg_color.split('@')
|
| 633 |
+
bg_color_name = bg_parts[0].lower()
|
| 634 |
bg_opacity = float(bg_parts[1]) if len(bg_parts) > 1 else 0.5
|
| 635 |
+
bg_color_rgb = COLOR_MAP.get(bg_color_name, "000000")
|
| 636 |
# Convert RGB to BGR for background
|
| 637 |
bg_color_bgr = bg_color_rgb[4:6] + bg_color_rgb[2:4] + bg_color_rgb[0:2]
|
| 638 |
bg_alpha = int((1 - bg_opacity) * 255)
|
|
|
|
| 737 |
print(f"β
Video styled successfully with ASS subtitles")
|
| 738 |
return True
|
| 739 |
|
|
|
|
| 740 |
# =============================================
|
| 741 |
# DEBUG ENDPOINTS
|
| 742 |
# =============================================
|
|
|
|
| 1025 |
except Exception as e:
|
| 1026 |
return {"error": str(e)}
|
| 1027 |
|
| 1028 |
+
@app.get("/debug/colors")
|
| 1029 |
+
async def list_colors():
|
| 1030 |
+
"""List all available colors"""
|
| 1031 |
+
return {
|
| 1032 |
+
"colors": list(COLOR_MAP.keys()),
|
| 1033 |
+
"examples": {
|
| 1034 |
+
"purple_variations": ["purple", "darkpurple", "lightpurple", "violet", "magenta"],
|
| 1035 |
+
"gold_variations": ["gold", "orange", "darkgold"],
|
| 1036 |
+
"basic": ["white", "black", "red", "green", "blue", "yellow"],
|
| 1037 |
+
"others": ["cyan", "pink", "brown", "gray", "navy", "teal", "maroon", "olive", "coral", "lavender", "turquoise", "indigo", "crimson"]
|
| 1038 |
+
}
|
| 1039 |
+
}
|
| 1040 |
+
|
| 1041 |
# =============================================
|
| 1042 |
# MAIN API ENDPOINTS
|
| 1043 |
# =============================================
|
|
|
|
| 1047 |
return {
|
| 1048 |
"status": "healthy",
|
| 1049 |
"fonts_loaded": len(FONTS),
|
| 1050 |
+
"colors_available": len(COLOR_MAP),
|
| 1051 |
"pil_available": PIL_AVAILABLE,
|
| 1052 |
"fonttools_available": FONTTOOLS_AVAILABLE,
|
| 1053 |
"whisper_available": WHISPER_AVAILABLE
|
|
|
|
| 1509 |
async def root():
|
| 1510 |
return {
|
| 1511 |
"name": "Text Styling API with Auto Caption",
|
| 1512 |
+
"version": "3.2.0",
|
| 1513 |
"features": {
|
| 1514 |
"title_overlay": "β
",
|
| 1515 |
"manual_captions": "β
",
|
| 1516 |
"auto_transcription": "β
" if WHISPER_AVAILABLE else "β",
|
| 1517 |
+
"srt_support": "β
",
|
| 1518 |
+
"enhanced_colors": f"β
({len(COLOR_MAP)} colors)"
|
| 1519 |
},
|
| 1520 |
"endpoints": {
|
| 1521 |
"style": "POST /api/style - Add title overlay",
|
|
|
|
| 1523 |
"caption_from_srt": "POST /api/caption-from-srt - Add captions from SRT URL",
|
| 1524 |
"transcribe": "POST /api/transcribe-and-caption - Auto-transcribe from audio",
|
| 1525 |
"fonts": "GET /fonts - List available fonts",
|
| 1526 |
+
"colors": "GET /debug/colors - List available colors",
|
| 1527 |
"health": "GET /health - Check status"
|
| 1528 |
},
|
| 1529 |
"debug": {
|
|
|
|
| 1531 |
"font_info": "GET /debug/font-info/{font_name}",
|
| 1532 |
"characters": "GET /debug/characters/{font_name}",
|
| 1533 |
"test_transcription": "POST /debug/test-transcription?audio_url=...&language=zh",
|
| 1534 |
+
"test_srt": "POST /debug/test-srt - Test SRT parsing",
|
| 1535 |
+
"colors": "GET /debug/colors - List all colors"
|
| 1536 |
},
|
| 1537 |
+
"fonts_loaded": len(FONTS),
|
| 1538 |
+
"colors_available": len(COLOR_MAP)
|
| 1539 |
}
|
| 1540 |
|
| 1541 |
# =============================================
|