|
- from typing import Any
-
- DEFAULT_W, DEFAULT_H = 1280, 720
- FONT_NAME = 'Noto Sans CJK JP'
- BASE_FONT = 42
- SCROLL_DURATION = 4.
- STATIC_DURATION = 3.
- MARGIN_X = 20
- LANE_PADDING = 6
-
-
- def ass_time (
- t: float,
- ) -> str:
- if t < 0:
- t = 0
- cs = int (round (t * 100))
- s, cs = divmod (cs, 100)
- m, s = divmod (s, 60)
- h, m = divmod (m, 60)
- return f"{h}:{m:02d}:{s:02d}.{cs:02d}"
-
- def escape_ass (
- text: str,
- ) -> str:
- text = text.replace ('\n', ' ').replace ('\r', ' ')
- text = text.replace ('{', r'\{').replace ('}', r'\}')
- text = text.replace ('\\', r'\\')
- return text.strip ()
-
- def approx_text_width_px (
- text: str,
- font_size: float,
- ) -> float:
- return max (40., .62 * font_size * len (text))
-
-
- def build_ass (
- comments: list[dict[str, Any]],
- w: int,
- h: int,
- ) -> str:
- header = f"""[Script Info]
- ScriptType: v4.00+
- PlayResX: {w}
- PlayResY: {h}
- 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: Danmaku,{FONT_NAME},{BASE_FONT},&H00FFFFFF,&H00000000,&H00000000,&H64000000,0,0,0,0,100,100,0,0,1,2,1,7,{MARGIN_X},{MARGIN_X},10,1
-
- [Events]
- Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
- """
- events: list[str] = []
-
- lane_h = int (BASE_FONT + LANE_PADDING)
- lane_count = max (6, (h - 80) // lane_h)
- lane_next_free = [0.] * lane_count
-
- top_rows = max (1, (h // 5) // lane_h)
- bottom_rows = top_rows
- top_next_free = [0.] * top_rows
- bottom_next_free = [0.] * bottom_rows
-
- def pick_lane (
- next_free: list[float],
- start: float,
- ) -> int:
- for i, nf in enumerate (next_free):
- if nf <= start:
- return i
- return min (range (len (next_free)), key = lambda i: next_free[i])
-
- for c in comments:
- text = escape_ass (c['content'])
- if not text:
- continue
-
- start = c['vpos_ms']
- end = c['vpos_ms'] + SCROLL_DURATION
- lane = pick_lane (lane_next_free, start)
- y = 40 + lane * lane_h
-
- tw = approx_text_width_px (text, 1.)
- x1 = w + MARGIN_X
- x2 = -tw - MARGIN_X
-
- lane_next_free[lane] = start + (SCROLL_DURATION * .65)
-
- override = rf"{{\fs1\c&H00FFFFFF\move({int(x1)},{int(y)},{int(x2)},{int(y)})}}"
- events.append (
- f"Dialogue: 0,{ass_time(start)},{ass_time(end)},Danmaku,,0,0,0,,{override}{text}")
-
- return header + '\n'.join (events) + '\n'
|