deeme commited on
Commit
0a6940a
·
verified ·
1 Parent(s): 2aac7b8

Upload 3 files

Browse files
Files changed (3) hide show
  1. Dockerfile +1 -1
  2. app.py +72 -15
  3. requirements.txt +2 -1
Dockerfile CHANGED
@@ -4,7 +4,7 @@ WORKDIR /app
4
 
5
  # 安装FFmpeg和Noto Sans CJK SC字体
6
  RUN apt-get update && \
7
- apt-get install -y ffmpeg fonts-noto-cjk fonts-noto-cjk-extra && \
8
  apt-get clean && \
9
  rm -rf /var/lib/apt/lists/*
10
 
 
4
 
5
  # 安装FFmpeg和Noto Sans CJK SC字体
6
  RUN apt-get update && \
7
+ apt-get install -y ffmpeg fonts-roboto && \
8
  apt-get clean && \
9
  rm -rf /var/lib/apt/lists/*
10
 
app.py CHANGED
@@ -16,6 +16,8 @@ import ssl
16
  import json
17
  from fastapi.staticfiles import StaticFiles
18
  from pydub import AudioSegment
 
 
19
 
20
  # 配置日志
21
  logging.basicConfig(level=logging.INFO)
@@ -104,7 +106,7 @@ def get_audio_duration(audio_path):
104
  # 创建caption字幕文件(底部显示)
105
  def create_caption_subtitle_file(project_dir, captions, panel_start_times, panel_durations):
106
  try:
107
- subtitle_file = os.path.join(project_dir, "captions.srt")
108
 
109
  with open(subtitle_file, "w", encoding="utf-8") as f:
110
  subtitle_index = 1
@@ -130,7 +132,7 @@ def create_caption_subtitle_file(project_dir, captions, panel_start_times, panel
130
  # 创建speech字幕文件(顶部显示)
131
  def create_speech_subtitle_file(project_dir, speeches, panel_start_times, panel_durations):
132
  try:
133
- subtitle_file = os.path.join(project_dir, "speeches.srt")
134
 
135
  with open(subtitle_file, "w", encoding="utf-8") as f:
136
  subtitle_index = 1
@@ -249,6 +251,16 @@ async def create_audio_file(project_dir, captions, speeches):
249
  logger.error(traceback.format_exc())
250
  return None, {}, [], []
251
 
 
 
 
 
 
 
 
 
 
 
252
  # 创建视频
253
  def create_video(project_dir, image_paths, caption_subtitle_file, speech_subtitle_file, audio_file, output_video, audio_durations, panel_start_times, panel_durations):
254
  try:
@@ -276,30 +288,75 @@ def create_video(project_dir, image_paths, caption_subtitle_file, speech_subtitl
276
  ]
277
  subprocess.run(cmd1, check=True)
278
 
279
- # 处理字幕路径转义(两个不同的字幕文件)
280
- caption_path = caption_subtitle_file.replace(os.sep, '/').replace(':', '\\:')
281
- speech_path = speech_subtitle_file.replace(os.sep, '/').replace(':', '\\:')
282
- # 合并滤镜的正确写法(使用两个subtitles滤镜)
283
- combined_filter = f"subtitles='{caption_path}':force_style='Alignment=2,WrapStyle=1,Fontsize=15,MarginV=30'," \
284
- f"subtitles='{speech_path}':force_style='Alignment=8,WrapStyle=1,Fontsize=15,MarginV=30'"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
285
  cmd_combined = [
286
  "ffmpeg", "-y",
287
  "-i", temp_video,
288
  "-vf", combined_filter,
289
  "-c:a", "copy",
290
- output_video # 直接输出到最终文件,无需temp_video2
 
 
 
 
 
 
291
  ]
292
 
 
 
 
293
  subprocess.run(cmd_combined, check=True)
294
-
295
- # 删除临时文件(只需删除temp_video)
296
  os.remove(temp_video)
297
-
298
  return output_video
299
  except Exception as e:
300
- logger.error(f"Error creating video: {e}")
301
- import traceback
302
- logger.error(traceback.format_exc())
303
  return None
304
 
305
  # 使用本地存储
 
16
  import json
17
  from fastapi.staticfiles import StaticFiles
18
  from pydub import AudioSegment
19
+ import shlex
20
+ from ffmpeg import probe as ffmpeg_probe # 需要安装ffmpeg-python包
21
 
22
  # 配置日志
23
  logging.basicConfig(level=logging.INFO)
 
106
  # 创建caption字幕文件(底部显示)
107
  def create_caption_subtitle_file(project_dir, captions, panel_start_times, panel_durations):
108
  try:
109
+ subtitle_file = os.path.join(project_dir, "captions.ass")
110
 
111
  with open(subtitle_file, "w", encoding="utf-8") as f:
112
  subtitle_index = 1
 
132
  # 创建speech字幕文件(顶部显示)
133
  def create_speech_subtitle_file(project_dir, speeches, panel_start_times, panel_durations):
134
  try:
135
+ subtitle_file = os.path.join(project_dir, "speeches.ass")
136
 
137
  with open(subtitle_file, "w", encoding="utf-8") as f:
138
  subtitle_index = 1
 
251
  logger.error(traceback.format_exc())
252
  return None, {}, [], []
253
 
254
+ def get_video_dimensions(video_path):
255
+ """获取视频分辨率"""
256
+ try:
257
+ probe = ffmpeg_probe(video_path)
258
+ video_stream = next(s for s in probe['streams'] if s['codec_type'] == 'video')
259
+ return int(video_stream['width']), int(video_stream['height'])
260
+ except Exception as e:
261
+ logger.warning(f"Can't detect video dimensions: {e}, using default 1920x1080")
262
+ return (1920, 1080)
263
+
264
  # 创建视频
265
  def create_video(project_dir, image_paths, caption_subtitle_file, speech_subtitle_file, audio_file, output_video, audio_durations, panel_start_times, panel_durations):
266
  try:
 
288
  ]
289
  subprocess.run(cmd1, check=True)
290
 
291
+ # 动态获取视频参数
292
+ video_width, video_height = get_video_dimensions(temp_video)
293
+ base_fontsize = max(24, video_width // 50)
294
+
295
+ # 智能样式计算
296
+ style_config = {
297
+ "caption": {
298
+ "fontsize": int(base_fontsize * 0.9),
299
+ "margin_v": video_height // 10,
300
+ "alignment": 2 # 底部居中
301
+ },
302
+ "speech": {
303
+ "fontsize": base_fontsize,
304
+ "margin_v": video_height // 12,
305
+ "alignment": 8 # 顶部居中
306
+ }
307
+ }
308
+ # 安全处理字幕路径
309
+ def process_sub_path(path):
310
+ return shlex.quote(
311
+ Path(path).resolve().as_posix()
312
+ .replace(':', '\\:')
313
+ )
314
+ # 构建复合滤镜
315
+ combined_filter = (
316
+ f"subtitles={process_sub_path(caption_subtitle_file)}:"
317
+ f"force_style="
318
+ f"'Fontname=Roboto-Bold,"
319
+ f"Fontsize={style_config['caption']['fontsize']},"
320
+ f"Alignment={style_config['caption']['alignment']},"
321
+ f"MarginV={style_config['caption']['margin_v']},"
322
+ f"Outline=1,Shadow=1,BackColour=&H40000000,"
323
+ f"BorderStyle=3,WordWrap=1,WrapStyle=2;"
324
+ f"subtitles={process_sub_path(speech_subtitle_file)}:"
325
+ f"force_style="
326
+ f"'Fontname=Roboto-Bold,"
327
+ f"Fontsize={style_config['speech']['fontsize']},"
328
+ f"Alignment={style_config['speech']['alignment']},"
329
+ f"MarginV={style_config['speech']['margin_v']},"
330
+ f"Outline=1,Shadow=1,BackColour=&H40000000,"
331
+ f"BorderStyle=1,WordWrap=1,WrapStyle=2'"
332
+ )
333
+ # 优化视频处理命令
334
  cmd_combined = [
335
  "ffmpeg", "-y",
336
  "-i", temp_video,
337
  "-vf", combined_filter,
338
  "-c:a", "copy",
339
+ "-c:v", "libx264",
340
+ "-preset", "fast",
341
+ "-movflags", "+faststart",
342
+ "-x264-params", "log-level=error",
343
+ "-hide_banner",
344
+ "-loglevel", "error",
345
+ output_video
346
  ]
347
 
348
+ # 执行并计时
349
+ logger.info("Starting video processing with optimized subtitles")
350
+ start_time = time.time()
351
  subprocess.run(cmd_combined, check=True)
352
+ logger.info(f"Video processing completed in {time.time()-start_time:.2f}s")
353
+ # 清理临时文件
354
  os.remove(temp_video)
355
+ Path(frames_list).unlink(missing_ok=True)
356
  return output_video
357
  except Exception as e:
358
+ logger.error(f"Video creation failed: {str(e)}")
359
+ logger.debug(f"Filter chain used: {combined_filter}")
 
360
  return None
361
 
362
  # 使用本地存储
requirements.txt CHANGED
@@ -4,4 +4,5 @@ aiohttp>=3.8.4
4
  openai>=1.2.0
5
  python-multipart>=0.0.6
6
  pydantic>=1.10.7
7
- pydub
 
 
4
  openai>=1.2.0
5
  python-multipart>=0.0.6
6
  pydantic>=1.10.7
7
+ pydub
8
+ ffmpeg-python