Files
meijiaka-zy/python-api/app/services/ass_generator.py
T

114 lines
3.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
ASS 字幕生成器
==============
生成带样式的 ASS 字幕文件,使用抖音美好体 (DouyinSans)。
"""
from __future__ import annotations
from app.schemas.caption import CaptionUtterance
def generate_ass(
utterances: list[CaptionUtterance],
video_width: int = 1080,
video_height: int = 1920,
fontname: str = "DouyinSansBold",
fontsize: float = 28.0,
primary_color: str = "&H00FFFFFF", # 白色
outline_color: str = "&H00000000", # 黑色描边
back_color: str = "&H80000000", # 半透明背景
outline: float = 2.0,
shadow: float = 1.0,
margin_v: int = 50,
) -> str:
"""
生成 ASS 字幕内容
Args:
utterances: 字幕时间轴列表
video_width: 视频宽度
video_height: 视频高度
fontname: 字体名称(默认为 DouyinSansBold
fontsize: 字体大小
primary_color: 主颜色 (&HAABBGGRR 格式)
outline_color: 描边颜色
back_color: 背景颜色
outline: 描边宽度
shadow: 阴影深度
margin_v: 垂直边距
Returns:
ASS 格式字符串
"""
# Script Info 部分
script_info = f"""[Script Info]
Title: Generated by Meijiaka AI Video
ScriptType: v4.00+
PlayResX: {video_width}
PlayResY: {video_height}
ScaledBorderAndShadow: yes
YCbCr Matrix: None
"""
# Styles 部分
styles = f"""[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,{fontname},{fontsize:.1f},{primary_color},&H000000FF,{outline_color},{back_color},0,0,0,0,100,100,0,0,1,{outline:.1f},{shadow:.1f},2,20,20,{margin_v},1
"""
# Events 部分
events_header = """[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
"""
events = []
for u in utterances:
start = _ms_to_ass_time(u.start_time)
end = _ms_to_ass_time(u.end_time)
# 转义特殊字符
text = u.text.replace("\\", "\\\\").replace("{", "\\{").replace("}", "\\}")
event = f"Dialogue: 0,{start},{end},Default,,0,0,0,,{text}"
events.append(event)
return script_info + styles + events_header + "\n".join(events)
def _ms_to_ass_time(ms: int) -> str:
"""毫秒转换为 ASS 时间格式 H:MM:SS.cc"""
hours = ms // 3600000
minutes = (ms % 3600000) // 60000
seconds = (ms % 60000) // 1000
centiseconds = (ms % 1000) // 10
return f"{hours}:{minutes:02d}:{seconds:02d}.{centiseconds:02d}"
# 预设样式
def generate_ass_short_video(utterances: list[CaptionUtterance]) -> str:
"""生成短视频风格的 ASS 字幕(竖屏 9:16)"""
return generate_ass(
utterances=utterances,
video_width=1080,
video_height=1920,
fontsize=32.0,
outline=2.5,
shadow=2.0,
margin_v=80,
)
def generate_ass_professional(utterances: list[CaptionUtterance]) -> str:
"""生成专业风格的 ASS 字幕(横屏 16:9)"""
return generate_ass(
utterances=utterances,
video_width=1920,
video_height=1080,
fontsize=24.0,
outline=1.5,
shadow=1.0,
margin_v=60,
)