qur / subtitle.py
digidau's picture
Update subtitle.py
b970631 verified
Raw
History Blame Contribute Delete
3.92 kB
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