Subtitles: convert SRT to ASS directly with PlayResY=1920 for predictable scaling instead of unreliable force_style
This commit is contained in:
parent
28d933c916
commit
e001387a89
@ -74,28 +74,88 @@ def transcribe(video, lang=None, model_size="small"):
|
||||
|
||||
SUBTITLE_STYLES = {
|
||||
"reels": (
|
||||
"FontName=Arial,FontSize=22,Bold=1,"
|
||||
"PrimaryColour=&H00FFFFFF,OutlineColour=&H00000000,BackColour=&H80000000,"
|
||||
"Outline=3,Shadow=0,Alignment=2,MarginV=320,BorderStyle=1"
|
||||
# Velike bele črke z debelim črnim outline-om, na spodnji tretjini
|
||||
"FontName=Arial,FontSize=42,Bold=1,"
|
||||
"PrimaryColour=&HFFFFFF,OutlineColour=&H000000,"
|
||||
"Outline=4,Shadow=1,Alignment=2,MarginV=120,BorderStyle=1"
|
||||
),
|
||||
"yellow": (
|
||||
"FontName=Arial,FontSize=22,Bold=1,"
|
||||
"PrimaryColour=&H0000FFFF,OutlineColour=&H00000000,"
|
||||
"Outline=3,Shadow=0,Alignment=2,MarginV=320,BorderStyle=1"
|
||||
"FontName=Arial,FontSize=42,Bold=1,"
|
||||
"PrimaryColour=&H00FFFF,OutlineColour=&H000000,"
|
||||
"Outline=4,Shadow=1,Alignment=2,MarginV=120,BorderStyle=1"
|
||||
),
|
||||
"minimal": (
|
||||
"FontName=Arial,FontSize=16,"
|
||||
"PrimaryColour=&H00FFFFFF,OutlineColour=&H80000000,"
|
||||
"Outline=2,Shadow=0,Alignment=2,MarginV=200,BorderStyle=1"
|
||||
"FontName=Arial,FontSize=28,"
|
||||
"PrimaryColour=&HFFFFFF,OutlineColour=&H000000,"
|
||||
"Outline=2,Shadow=0,Alignment=2,MarginV=80,BorderStyle=1"
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def burn_subtitles(video, srt, output, style="reels"):
|
||||
style_str = SUBTITLE_STYLES.get(style, SUBTITLE_STYLES["reels"])
|
||||
# Escape srt path za FFmpeg subtitles filter
|
||||
srt_escaped = srt.replace("\\", "\\\\").replace(":", "\\:").replace("'", r"\'")
|
||||
vf = f"subtitles='{srt_escaped}':force_style='{style_str}'"
|
||||
"""Burn-in podnapisov. Najprej pretvorimo SRT v ASS z eksplicitnim stylom, ker
|
||||
FFmpeg force_style je ne-zanesljivo in pogosto silently ignore-an."""
|
||||
# Pretvorimo SRT → ASS s pravim stylom
|
||||
ass_path = srt.replace(".srt", ".ass") if srt.endswith(".srt") else srt + ".ass"
|
||||
|
||||
# Style nastavitve glede na izbiro
|
||||
if style == "yellow":
|
||||
primary = "&H0000FFFF" # rumeno
|
||||
else:
|
||||
primary = "&H00FFFFFF" # belo
|
||||
|
||||
# ASS PlayResY 1920 → MarginV je v pikslih 1:1
|
||||
ass_header = f"""[Script Info]
|
||||
ScriptType: v4.00+
|
||||
PlayResX: 1080
|
||||
PlayResY: 1920
|
||||
WrapStyle: 2
|
||||
ScaledBorderAndShadow: yes
|
||||
|
||||
[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: Default,Arial,84,{primary},&H00FFFFFF,&H00000000,&H00000000,1,0,0,0,100,100,0,0,1,8,0,2,40,40,200,1
|
||||
|
||||
[Events]
|
||||
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
|
||||
"""
|
||||
|
||||
# Parse SRT in convert v ASS dialogue lines
|
||||
with open(srt, "r", encoding="utf-8") as f:
|
||||
srt_content = f.read()
|
||||
|
||||
def srt_to_ass_time(t):
|
||||
# 00:00:09,520 → 0:00:09.52
|
||||
h, m, rest = t.split(":")
|
||||
s, ms = rest.split(",")
|
||||
return f"{int(h)}:{int(m):02d}:{int(s):02d}.{int(ms)//10:02d}"
|
||||
|
||||
dialogue_lines = []
|
||||
blocks = srt_content.strip().split("\n\n")
|
||||
for block in blocks:
|
||||
lines = block.strip().split("\n")
|
||||
if len(lines) < 3:
|
||||
continue
|
||||
# lines[0] = idx, lines[1] = timecode, lines[2:] = text
|
||||
timecode = lines[1]
|
||||
text = " ".join(lines[2:]).replace("\n", " ")
|
||||
if " --> " not in timecode:
|
||||
continue
|
||||
start_t, end_t = timecode.split(" --> ")
|
||||
ass_start = srt_to_ass_time(start_t.strip())
|
||||
ass_end = srt_to_ass_time(end_t.strip())
|
||||
dialogue_lines.append(f"Dialogue: 0,{ass_start},{ass_end},Default,,0,0,0,,{text}")
|
||||
|
||||
with open(ass_path, "w", encoding="utf-8") as f:
|
||||
f.write(ass_header)
|
||||
f.write("\n".join(dialogue_lines))
|
||||
f.write("\n")
|
||||
|
||||
print(f"📝 ASS: {ass_path} ({len(dialogue_lines)} dialogov)")
|
||||
|
||||
# Burn-in z ass filtrom (boljši kot subtitles za naš primer)
|
||||
ass_escaped = ass_path.replace("\\", "\\\\").replace(":", "\\:").replace("'", r"\'")
|
||||
vf = f"ass='{ass_escaped}'"
|
||||
|
||||
cmd = [
|
||||
"ffmpeg", "-y", "-i", str(video),
|
||||
|
||||
Loading…
Reference in New Issue
Block a user