Ryanus commited on
Commit
c423fd6
·
verified ·
1 Parent(s): 3a34864

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +63 -31
app.py CHANGED
@@ -7,6 +7,7 @@ import shutil
7
  import zipfile
8
  from datetime import datetime
9
  import json
 
10
 
11
  # 储存目录设置
12
  STORAGE_DIR = os.path.expanduser("~/video_storage")
@@ -26,7 +27,7 @@ def init_storage():
26
  json.dump(config, f, ensure_ascii=False, indent=2)
27
 
28
  def save_to_storage(file_path, metadata=None):
29
- """保存文件到储存空间"""
30
  try:
31
  base_name = os.path.basename(file_path)
32
  target_path = os.path.join(STORAGE_DIR, base_name)
@@ -39,7 +40,7 @@ def save_to_storage(file_path, metadata=None):
39
  count += 1
40
 
41
  shutil.copy2(file_path, target_path)
42
- update_storage_config()
43
  return target_path
44
  except Exception as e:
45
  print(f"❌ 储存文件失败: {e}")
@@ -212,17 +213,17 @@ def clear_storage():
212
  return f"❌ 清空失败: {str(e)}"
213
 
214
  def ffmpeg_cut_video(input_path, start_time, duration, output_path):
215
- """稳定的视频切割"""
216
  command = [
217
  'ffmpeg', '-i', input_path, '-ss', str(start_time), '-t', str(duration),
218
- '-c:v', 'libx264', '-preset', 'medium', '-crf', '23',
219
  '-c:a', 'aac', '-b:a', '128k', '-avoid_negative_ts', 'make_zero', '-y', output_path
220
  ]
221
  result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
222
  return result.returncode == 0 and os.path.exists(output_path)
223
 
224
  def ffmpeg_resize_video(input_path, output_path, target_ratio):
225
- """稳定的比例调整"""
226
  if target_ratio == '9:16':
227
  filter_complex = "scale=1080:1920:force_original_aspect_ratio=decrease,pad=1080:1920:(ow-iw)/2:(oh-ih)/2:black"
228
  else:
@@ -230,7 +231,7 @@ def ffmpeg_resize_video(input_path, output_path, target_ratio):
230
 
231
  command = [
232
  'ffmpeg', '-i', input_path, '-vf', filter_complex,
233
- '-c:v', 'libx264', '-preset', 'medium', '-crf', '23', '-c:a', 'copy', '-y', output_path
234
  ]
235
  result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
236
  return result.returncode == 0 and os.path.exists(output_path)
@@ -260,8 +261,38 @@ def concat_videos(file_list, output_path):
260
  except:
261
  pass
262
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
263
  def process_videos_with_storage(video_files, clip_duration, num_output_videos, target_ratio):
264
- """带储存功能的视频处理"""
265
  if not video_files:
266
  return "❌ 请上传视频文件", None, "", ""
267
 
@@ -269,36 +300,32 @@ def process_videos_with_storage(video_files, clip_duration, num_output_videos, t
269
 
270
  try:
271
  all_clips = []
272
-
273
- for idx, video_file in enumerate(video_files):
274
- video_path = video_file.name
275
-
276
- try:
277
- cmd = ['ffprobe', '-v', 'quiet', '-show_entries', 'format=duration', '-of', 'default=noprint_wrappers=1:nokey=1', video_path]
278
- result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
279
- total_duration = float(result.stdout.strip())
280
- except:
281
- continue
282
 
283
- start = 0.0
284
- count = 0
285
- while start < total_duration:
286
- duration = min(clip_duration, total_duration - start)
287
- clip_path = os.path.join(temp_dir, f"clip_{idx}_{count}.mp4")
288
-
289
- if ffmpeg_cut_video(video_path, start, duration, clip_path):
290
- all_clips.append(clip_path)
291
-
292
- start += clip_duration
293
- count += 1
294
-
295
  if not all_clips:
296
  return "❌ 切割失败,请检查视频文件", None, "", ""
297
 
298
  random.shuffle(all_clips)
299
  clips_per_video = max(1, len(all_clips) // num_output_videos)
300
  output_files = []
301
- stored_files = []
302
 
303
  for i in range(num_output_videos):
304
  start_idx = i * clips_per_video
@@ -321,6 +348,11 @@ def process_videos_with_storage(video_files, clip_duration, num_output_videos, t
321
  if stored_path:
322
  stored_files.append(os.path.basename(stored_path))
323
 
 
 
 
 
 
324
  if not output_files:
325
  return "❌ 生成混剪视频失败", None, "", ""
326
 
@@ -632,4 +664,4 @@ def main():
632
  demo.launch()
633
 
634
  if __name__ == "__main__":
635
- main()
 
7
  import zipfile
8
  from datetime import datetime
9
  import json
10
+ import concurrent.futures # 新增:用于并行处理
11
 
12
  # 储存目录设置
13
  STORAGE_DIR = os.path.expanduser("~/video_storage")
 
27
  json.dump(config, f, ensure_ascii=False, indent=2)
28
 
29
  def save_to_storage(file_path, metadata=None):
30
+ """保存文件到储存空间 [优化:移除即时更新配置]"""
31
  try:
32
  base_name = os.path.basename(file_path)
33
  target_path = os.path.join(STORAGE_DIR, base_name)
 
40
  count += 1
41
 
42
  shutil.copy2(file_path, target_path)
43
+ # update_storage_config() # 移除这行,在批量操作后统一更新
44
  return target_path
45
  except Exception as e:
46
  print(f"❌ 储存文件失败: {e}")
 
213
  return f"❌ 清空失败: {str(e)}"
214
 
215
  def ffmpeg_cut_video(input_path, start_time, duration, output_path):
216
+ """[优化] 快速的视频切割,使用 superfast 预设"""
217
  command = [
218
  'ffmpeg', '-i', input_path, '-ss', str(start_time), '-t', str(duration),
219
+ '-c:v', 'libx264', '-preset', 'superfast', '-crf', '28', # 修改:更快的编码
220
  '-c:a', 'aac', '-b:a', '128k', '-avoid_negative_ts', 'make_zero', '-y', output_path
221
  ]
222
  result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
223
  return result.returncode == 0 and os.path.exists(output_path)
224
 
225
  def ffmpeg_resize_video(input_path, output_path, target_ratio):
226
+ """[优化] 快速的比例调整,使用 superfast 预设"""
227
  if target_ratio == '9:16':
228
  filter_complex = "scale=1080:1920:force_original_aspect_ratio=decrease,pad=1080:1920:(ow-iw)/2:(oh-ih)/2:black"
229
  else:
 
231
 
232
  command = [
233
  'ffmpeg', '-i', input_path, '-vf', filter_complex,
234
+ '-c:v', 'libx264', '-preset', 'superfast', '-crf', '28', '-c:a', 'copy', '-y', output_path # 修改:更快的编码
235
  ]
236
  result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
237
  return result.returncode == 0 and os.path.exists(output_path)
 
261
  except:
262
  pass
263
 
264
+ def process_single_video(video_file, clip_duration, temp_dir):
265
+ """[优化] 处理单个视频文件,返回其所有切片路径的列表"""
266
+ video_path = video_file.name
267
+ clips = []
268
+ try:
269
+ # 获取视频总时长
270
+ cmd = ['ffprobe', '-v', 'quiet', '-show_entries', 'format=duration', '-of', 'default=noprint_wrappers=1:nokey=1', video_path]
271
+ result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
272
+ total_duration = float(result.stdout.strip())
273
+ except Exception as e:
274
+ print(f"处理视频 {video_path} 时获取时长失败: {e}")
275
+ return clips
276
+
277
+ start = 0.0
278
+ count = 0
279
+ while start < total_duration:
280
+ duration = min(clip_duration, total_duration - start)
281
+ # 修改文件名,包含原始文件名和计数,避免不同视频的切片重名
282
+ clip_path = os.path.join(temp_dir, f"clip_{os.path.splitext(os.path.basename(video_path))[0]}_{count}.mp4")
283
+
284
+ if ffmpeg_cut_video(video_path, start, duration, clip_path):
285
+ clips.append(clip_path)
286
+ else:
287
+ print(f"切割视频 {video_path} 的第 {count} 个片段失败")
288
+
289
+ start += clip_duration
290
+ count += 1
291
+
292
+ return clips
293
+
294
  def process_videos_with_storage(video_files, clip_duration, num_output_videos, target_ratio):
295
+ """带储存功能的视频处理 [优化版:并行切割 + 批量更新]"""
296
  if not video_files:
297
  return "❌ 请上传视频文件", None, "", ""
298
 
 
300
 
301
  try:
302
  all_clips = []
303
+
304
+ # --- [优化] 使用线程池并行处理每个视频文件 ---
305
+ with concurrent.futures.ThreadPoolExecutor(max_workers=min(4, os.cpu_count() or 1)) as executor:
306
+ # 提交所有任务
307
+ future_to_video = {
308
+ executor.submit(process_single_video, vf, clip_duration, temp_dir): vf
309
+ for vf in video_files
310
+ }
 
 
311
 
312
+ # 收集所有切片
313
+ for future in concurrent.futures.as_completed(future_to_video):
314
+ try:
315
+ video_clips = future.result()
316
+ all_clips.extend(video_clips)
317
+ except Exception as exc:
318
+ video_file = future_to_video[future]
319
+ print(f'处理视频 {video_file.name} 时发生异常: {exc}')
320
+ # --- 并行处理结束 ---
321
+
 
 
322
  if not all_clips:
323
  return "❌ 切割失败,请检查视频文件", None, "", ""
324
 
325
  random.shuffle(all_clips)
326
  clips_per_video = max(1, len(all_clips) // num_output_videos)
327
  output_files = []
328
+ stored_files = [] # 用于记录成功保存的文件名
329
 
330
  for i in range(num_output_videos):
331
  start_idx = i * clips_per_video
 
348
  if stored_path:
349
  stored_files.append(os.path.basename(stored_path))
350
 
351
+ # --- [优化] 所有文件保存后,统一更新一次配置 ---
352
+ if stored_files: # 只有当有文件被成功储存时才更新
353
+ update_storage_config()
354
+ # --- 优化结束 ---
355
+
356
  if not output_files:
357
  return "❌ 生成混剪视频失败", None, "", ""
358
 
 
664
  demo.launch()
665
 
666
  if __name__ == "__main__":
667
+ main()