Upload app.py
Browse files
app.py
CHANGED
|
@@ -104,26 +104,50 @@ def get_audio_duration(audio_path):
|
|
| 104 |
logger.error(f"Error getting audio duration: {e}")
|
| 105 |
return 5.0 # 默认5秒
|
| 106 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 107 |
# 创建caption字幕文件(底部显示)
|
| 108 |
def create_caption_subtitle_file(project_dir, captions, panel_start_times, panel_durations):
|
| 109 |
try:
|
| 110 |
-
subtitle_file = os.path.join(project_dir, "captions.ass")
|
| 111 |
-
|
| 112 |
with open(subtitle_file, "w", encoding="utf-8") as f:
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
end_time = start_time + duration
|
| 121 |
-
|
| 122 |
-
# 字幕显示整个面板的时长
|
| 123 |
-
f.write(f"{subtitle_index}\n")
|
| 124 |
-
f.write(f"{format_time(start_time)} --> {format_time(end_time)}\n")
|
| 125 |
-
f.write(f"{caption}\n\n")
|
| 126 |
-
subtitle_index += 1
|
| 127 |
|
| 128 |
return subtitle_file
|
| 129 |
except Exception as e:
|
|
@@ -133,23 +157,14 @@ def create_caption_subtitle_file(project_dir, captions, panel_start_times, panel
|
|
| 133 |
# 创建speech字幕文件(顶部显示)
|
| 134 |
def create_speech_subtitle_file(project_dir, speeches, panel_start_times, panel_durations):
|
| 135 |
try:
|
| 136 |
-
subtitle_file = os.path.join(project_dir, "speeches.ass")
|
| 137 |
-
|
| 138 |
with open(subtitle_file, "w", encoding="utf-8") as f:
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
end_time = start_time + duration
|
| 147 |
-
|
| 148 |
-
# 字幕显示整个面板的时长
|
| 149 |
-
f.write(f"{subtitle_index}\n")
|
| 150 |
-
f.write(f"{format_time(start_time)} --> {format_time(end_time)}\n")
|
| 151 |
-
f.write(f"{speech}\n\n")
|
| 152 |
-
subtitle_index += 1
|
| 153 |
|
| 154 |
return subtitle_file
|
| 155 |
except Exception as e:
|
|
@@ -317,7 +332,7 @@ def create_video(project_dir, image_paths, caption_subtitle_file, speech_subtitl
|
|
| 317 |
combined_filter = (
|
| 318 |
f"subtitles={process_sub_path(caption_subtitle_file)}:"
|
| 319 |
"force_style='"
|
| 320 |
-
"FontName=Noto Sans CJK SC,"
|
| 321 |
"Fontsize={},"
|
| 322 |
"Alignment=2,"
|
| 323 |
"MarginV={},"
|
|
@@ -327,7 +342,7 @@ def create_video(project_dir, image_paths, caption_subtitle_file, speech_subtitl
|
|
| 327 |
),
|
| 328 |
f"subtitles={process_sub_path(speech_subtitle_file)}:"
|
| 329 |
"force_style='"
|
| 330 |
-
"FontName=Noto Sans CJK SC,"
|
| 331 |
"Fontsize={},"
|
| 332 |
"Alignment=8,"
|
| 333 |
"MarginV={},"
|
|
|
|
| 104 |
logger.error(f"Error getting audio duration: {e}")
|
| 105 |
return 5.0 # 默认5秒
|
| 106 |
|
| 107 |
+
# 在生成ASS文件时添加完整样式头
|
| 108 |
+
ASS_STYLE_HEADER = """[Script Info]
|
| 109 |
+
WrapStyle: 0
|
| 110 |
+
ScaledBorderAndShadow: yes
|
| 111 |
+
PlayResX: 1920
|
| 112 |
+
PlayResY: 1080
|
| 113 |
+
[V4+ Styles]
|
| 114 |
+
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
|
| 115 |
+
Style: Caption,Noto Sans CJK SC,54,&H00FFFFFF,&H000000FF,&H00333333,&H00000000,0,0,0,0,100,100,0,0,1,2,3,2,100,100,50,0
|
| 116 |
+
Style: Speech,Noto Sans CJK SC,48,&H00FFFFFF,&H000000FF,&H00333333,&H00000000,0,0,0,0,100,100,0,0,1,2,3,8,100,100,50,0
|
| 117 |
+
[Events]
|
| 118 |
+
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
|
| 119 |
+
"""
|
| 120 |
+
def smart_wrap(text, video_width=1920):
|
| 121 |
+
"""智能换行算法"""
|
| 122 |
+
max_chars = int(video_width // 38) # 基于典型字体尺寸计算
|
| 123 |
+
lines = []
|
| 124 |
+
current_line = []
|
| 125 |
+
current_len = 0
|
| 126 |
+
|
| 127 |
+
for char in text:
|
| 128 |
+
char_len = 2 if ord(char) > 255 else 1 # 全角字符计为2单位
|
| 129 |
+
if current_len + char_len > max_chars * 2:
|
| 130 |
+
lines.append(''.join(current_line))
|
| 131 |
+
current_line = [char]
|
| 132 |
+
current_len = char_len
|
| 133 |
+
else:
|
| 134 |
+
current_line.append(char)
|
| 135 |
+
current_len += char_len
|
| 136 |
+
lines.append(''.join(current_line))
|
| 137 |
+
|
| 138 |
+
return r'\N'.join(lines)
|
| 139 |
+
|
| 140 |
# 创建caption字幕文件(底部显示)
|
| 141 |
def create_caption_subtitle_file(project_dir, captions, panel_start_times, panel_durations):
|
| 142 |
try:
|
|
|
|
|
|
|
| 143 |
with open(subtitle_file, "w", encoding="utf-8") as f:
|
| 144 |
+
f.write(ASS_STYLE_HEADER)
|
| 145 |
+
for i, (caption, start, duration) in enumerate(zip(captions, panel_start_times, panel_durations)):
|
| 146 |
+
wrapped_text = smart_wrap(caption)
|
| 147 |
+
f.write(
|
| 148 |
+
f"Dialogue: 0,{format_time(start)},{format_time(start + duration)},"
|
| 149 |
+
f"Caption,,0,0,0,,{wrapped_text}\n"
|
| 150 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 151 |
|
| 152 |
return subtitle_file
|
| 153 |
except Exception as e:
|
|
|
|
| 157 |
# 创建speech字幕文件(顶部显示)
|
| 158 |
def create_speech_subtitle_file(project_dir, speeches, panel_start_times, panel_durations):
|
| 159 |
try:
|
|
|
|
|
|
|
| 160 |
with open(subtitle_file, "w", encoding="utf-8") as f:
|
| 161 |
+
f.write(ASS_STYLE_HEADER)
|
| 162 |
+
for i, (speech, start, duration) in enumerate(zip(speeches, panel_start_times, panel_durations)):
|
| 163 |
+
wrapped_text = smart_wrap(speech)
|
| 164 |
+
f.write(
|
| 165 |
+
f"Dialogue: 0,{format_time(start)},{format_time(start + duration)},"
|
| 166 |
+
f"Speech,,0,0,0,,{wrapped_text}\n"
|
| 167 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 168 |
|
| 169 |
return subtitle_file
|
| 170 |
except Exception as e:
|
|
|
|
| 332 |
combined_filter = (
|
| 333 |
f"subtitles={process_sub_path(caption_subtitle_file)}:"
|
| 334 |
"force_style='"
|
| 335 |
+
#"FontName=Noto Sans CJK SC,"
|
| 336 |
"Fontsize={},"
|
| 337 |
"Alignment=2,"
|
| 338 |
"MarginV={},"
|
|
|
|
| 342 |
),
|
| 343 |
f"subtitles={process_sub_path(speech_subtitle_file)}:"
|
| 344 |
"force_style='"
|
| 345 |
+
#"FontName=Noto Sans CJK SC,"
|
| 346 |
"Fontsize={},"
|
| 347 |
"Alignment=8,"
|
| 348 |
"MarginV={},"
|