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

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +43 -54
app.py CHANGED
@@ -18,6 +18,7 @@ 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)
@@ -252,17 +253,33 @@ async def create_audio_file(project_dir, captions, speeches):
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:
267
  # 创建帧列表文件 - 使用精确的面板持续时间
268
  frames_list = os.path.join(project_dir, "frames.txt")
@@ -288,75 +305,47 @@ def create_video(project_dir, image_paths, caption_subtitle_file, speech_subtitl
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
  # 使用本地存储
 
18
  from pydub import AudioSegment
19
  import shlex
20
  from ffmpeg import probe as ffmpeg_probe # 需要安装ffmpeg-python包
21
+ import time
22
 
23
  # 配置日志
24
  logging.basicConfig(level=logging.INFO)
 
253
  return None, {}, [], []
254
 
255
  def get_video_dimensions(video_path):
 
256
  try:
257
+ result = subprocess.run(
258
+ ["ffprobe", "-v", "error", "-select_streams", "v:0",
259
+ "-show_entries", "stream=width,height", "-of", "json", video_path],
260
+ capture_output=True,
261
+ text=True
262
+ )
263
+ data = json.loads(result.stdout)
264
+ return (int(data['streams'][0]['width']),
265
+ int(data['streams'][0]['height']))
266
  except Exception as e:
267
+ logger.warning(f"Video dimension detection failed: {e}")
268
  return (1920, 1080)
269
 
270
+ def process_sub_path(path):
271
+ # 统一处理所有特殊字符
272
+ return shlex.quote(
273
+ str(Path(path).resolve())
274
+ .replace(':', '\\:')
275
+ .replace(' ', '\\ ')
276
+ .replace('(', '\\(')
277
+ .replace(')', '\\)')
278
+ )
279
+
280
  # 创建视频
281
+ def create_video(project_dir, image_paths, caption_subtitle_file, speech_subtitle_file,
282
+ audio_file, output_video, audio_durations, panel_start_times, panel_durations):
283
  try:
284
  # 创建帧列表文件 - 使用精确的面板持续时间
285
  frames_list = os.path.join(project_dir, "frames.txt")
 
305
  ]
306
  subprocess.run(cmd1, check=True)
307
 
308
+ # 获取视频尺寸(使用改进后的方法)
309
  video_width, video_height = get_video_dimensions(temp_video)
310
  base_fontsize = max(24, video_width // 50)
311
+ # 构建滤镜链
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
312
  combined_filter = (
313
  f"subtitles={process_sub_path(caption_subtitle_file)}:"
314
+ "force_style='Fontsize={},Alignment=2,MarginV={},Outline=1'".format(
315
+ int(base_fontsize*0.9),
316
+ video_height//10
317
+ ),
 
 
 
318
  f"subtitles={process_sub_path(speech_subtitle_file)}:"
319
+ "force_style='Fontsize={},Alignment=8,MarginV={},Outline=1'".format(
320
+ base_fontsize,
321
+ video_height//12
322
+ )
 
 
 
323
  )
324
+ filter_chain = ",".join(combined_filter)
325
+ # 优化ffmpeg命令
326
  cmd_combined = [
327
  "ffmpeg", "-y",
328
  "-i", temp_video,
329
+ "-vf", filter_chain,
330
  "-c:a", "copy",
331
  "-c:v", "libx264",
332
  "-preset", "fast",
333
  "-movflags", "+faststart",
 
 
 
334
  output_video
335
  ]
336
+ # 添加执行计时
 
 
337
  start_time = time.time()
338
  subprocess.run(cmd_combined, check=True)
339
+ logger.info(f"Video processed in {time.time()-start_time:.2f}s")
340
  # 清理临时文件
341
  os.remove(temp_video)
 
342
  return output_video
343
+ except subprocess.CalledProcessError as e:
344
+ logger.error(f"FFmpeg failed with cmd: {' '.join(e.cmd)}")
345
+ logger.error(f"FFmpeg stderr: {e.stderr}")
346
+ return None
347
  except Exception as e:
348
+ logger.error(f"Unexpected error: {str(e)}")
 
349
  return None
350
 
351
  # 使用本地存储