File size: 2,439 Bytes
2e31ab2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import os
from typing import List, Dict


def _ass_time(t: float) -> str:
    """Helper function to format time for ASS files (H:MM:SS.cs)."""
    if t < 0:
        t = 0
    h = int(t // 3600)
    m = int((t % 3600) // 60)
    s = int(t % 60)
    cs = int((t - int(t)) * 100)
    return f"{h}:{m:02}:{s:02}.{cs:02}"


def generate_subtitle_file(
    tokens_with_timestamps: List[Dict], output_audio_path: str
) -> str:
    """
    Generates an ASS subtitle file from tokens with absolute timestamps.

    Args:
        tokens_with_timestamps (list): A list of token dictionaries with 'text', 'start', and 'end' keys.
        output_audio_path (str): The path to the audio file, used to name the subtitle file.

    Returns:
        str: The path to the generated subtitle file.
    """
    subtitle_file_path = os.path.splitext(output_audio_path)[0] + ".ass"

    with open(subtitle_file_path, "w", encoding="utf-8") as f:
        # Write standard ASS header
        f.write("[Script Info]\n")
        f.write("Title: Generated by Manimator\n")
        f.write("ScriptType: v4.00+\n\n")
        f.write("[V4+ Styles]\n")
        f.write(
            "Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding\n"
        )
        f.write(
            "Style: Default,Arial,24,&H00FFFFFF,&H000000FF,&H003C3C3C,&H00000000,0,0,0,0,100,100,0,0,1,1.5,1,2,10,10,15,1\n\n"
        )
        f.write("[Events]\n")
        f.write(
            "Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\n"
        )

        # Write dialogue entries word-by-word
        for token in tokens_with_timestamps:
            start_time = token.get("start")
            end_time = token.get("end")
            text = token.get("text", "").strip()

            if (
                start_time is not None
                and end_time is not None
                and text
                and end_time > start_time
            ):
                start_formatted = _ass_time(start_time)
                end_formatted = _ass_time(end_time)
                text = text.replace(",", "\\,")
                f.write(
                    f"Dialogue: 0,{start_formatted},{end_formatted},Default,,0,0,0,,{text}\n"
                )

    return subtitle_file_path