diff --git a/scripts/subtitle.py b/scripts/subtitle.py index 5156175..865178a 100644 --- a/scripts/subtitle.py +++ b/scripts/subtitle.py @@ -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),