import json from font_utils import build_font_map def sec_to_ass(seconds): h = int(seconds // 3600) m = int((seconds % 3600) // 60) s = seconds % 60 return f"{h}:{m:02d}:{s:05.2f}" def ass_escape(text): if not text: return "" return ( text.replace("\\", "\\\\") .replace("{", "\\{") .replace("}", "\\}") ) def wrap_text( text, width=40, max_lines=2 ): if not text: return "" words = text.split() lines = [] current = [] for word in words: candidate = " ".join( current + [word] ) if len(candidate) > width: if current: lines.append( " ".join(current) ) current = [word] else: current.append(word) if current: lines.append( " ".join(current) ) if len(lines) <= max_lines: return r"\N".join(lines) merged = [] per = ( len(lines) + max_lines - 1 ) // max_lines for i in range( 0, len(lines), per ): merged.append( " ".join( lines[i:i + per] ) ) return r"\N".join(merged) def generate_ass( json_file, output_ass, arabic_font, arabic_y=380, translation_gap=25, arabic_font_size=120, translation_font_size=34 ): with open( json_file, "r", encoding="utf-8" ) as f: data = json.load(f) font_map = build_font_map() font_name = font_map.get( arabic_font, arabic_font ) print("=" * 60) print("FONT DEBUG") print("=" * 60) print("SELECTED FONT:") print(arabic_font) print("FONT FAMILY:") print(font_name) header = f""" [Script Info] Title: Quran Video ScriptType: v4.00+ PlayResX: 1920 PlayResY: 1080 ScaledBorderAndShadow: yes WrapStyle: 2 [V4+ Styles] Format: Name,Fontname,Fontsize,PrimaryColour,SecondaryColour,OutlineColour,BackColour,Bold,Italic,Underline,StrikeOut,ScaleX,ScaleY,Spacing,Angle,BorderStyle,Outline,Shadow,Alignment,MarginL,MarginR,MarginV,Encoding Style: Arabic,{font_name},{arabic_font_size},&H00FFFFFF,&H000000FF,&H18000000,&H00000000,0,0,0,0,100,100,0,0,1,0.5,2,2,0,0,0,1 Style: Translation,Arial,{translation_font_size},&H00FFFFFF,&H000000FF,&H18000000,&H00000000,0,0,0,0,100,100,0,0,1,0.3,2,2,0,0,0,1 [Events] Format: Layer,Start,End,Style,Name,MarginL,MarginR,MarginV,Effect,Text """ events = [] for item in data: start = sec_to_ass( item["start_time"] ) end = sec_to_ass( item["end_time"] ) arabic = wrap_text( ass_escape( item["text"] ), width=120, max_lines=2 ) translation = wrap_text( ass_escape( item["translation"] ), width=80, max_lines=2 ) arabic_lines = arabic.count(r"\N") + 1 line_height = 90 translation_y = ( arabic_y + (arabic_lines * line_height) + translation_gap ) events.append( f"Dialogue: 0,{start},{end},Arabic,,0,0,0,," f"{{\\an8\\pos(960,{arabic_y})}}" f"{arabic}" ) events.append( f"Dialogue: 0,{start},{end},Translation,,0,0,0,," f"{{\\an8\\pos(960,{translation_y})}}" f"{translation}" ) with open( output_ass, "w", encoding="utf-8" ) as f: f.write(header) f.write("\n".join(events)) print("=" * 60) print("ASS GENERATED") print("=" * 60) print( f"Font used: {font_name}" ) return output_ass