openfree commited on
Commit
034130a
ยท
verified ยท
1 Parent(s): d7a92e0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +103 -16
app.py CHANGED
@@ -35,6 +35,8 @@ from transformers import pipeline
35
  from transformers import AutoModelForImageSegmentation
36
  from torchvision import transforms
37
  from moviepy import VideoFileClip, vfx, concatenate_videoclips, ImageSequenceClip, concatenate_audioclips, AudioFileClip, CompositeAudioClip
 
 
38
  import time
39
  from concurrent.futures import ThreadPoolExecutor
40
 
@@ -628,12 +630,46 @@ def merge_videos_with_audio(video_files, audio_file, audio_volume, output_fps):
628
 
629
  status = f"{len(video_paths)}๊ฐœ์˜ ๋น„๋””์˜ค ๋กœ๋“œ ์ค‘..."
630
 
631
- # ๋น„๋””์˜ค ํด๋ฆฝ ๋กœ๋“œ
632
  video_clips = []
 
 
 
633
  for i, video_path in enumerate(video_paths):
634
  status = f"๋น„๋””์˜ค {i+1}/{len(video_paths)} ๋กœ๋“œ ์ค‘: {os.path.basename(video_path)}"
635
  clip = VideoFileClip(video_path)
636
  video_clips.append(clip)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
637
 
638
  # ์ฒซ ๋ฒˆ์งธ ๋น„๋””์˜ค์˜ FPS๋ฅผ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์‚ฌ์šฉ
639
  if output_fps == 0:
@@ -642,22 +678,31 @@ def merge_videos_with_audio(video_files, audio_file, audio_volume, output_fps):
642
  status = "๋น„๋””์˜ค ๋ณ‘ํ•ฉ ์ค‘..."
643
 
644
  # ๋น„๋””์˜ค ๋ณ‘ํ•ฉ
645
- final_video = concatenate_videoclips(video_clips, method="compose")
646
 
647
  # ์˜ค๋””์˜ค ์ฒ˜๋ฆฌ
648
  if audio_file:
649
  status = "์˜ค๋””์˜ค ์ฒ˜๋ฆฌ ์ค‘..."
650
 
651
  try:
 
 
 
 
 
 
 
 
 
652
  # ์˜ค๋””์˜ค ๋กœ๋“œ
653
- if audio_file.endswith(('.mp4', '.avi', '.mov', '.mkv')):
654
  # ๋น„๋””์˜ค ํŒŒ์ผ์—์„œ ์˜ค๋””์˜ค ์ถ”์ถœ
655
- temp_video = VideoFileClip(audio_file)
656
  audio_clip = temp_video.audio
657
  temp_video.close()
658
  else:
659
  # ์˜ค๋””์˜ค ํŒŒ์ผ ์ง์ ‘ ๋กœ๋“œ
660
- audio_clip = AudioFileClip(audio_file)
661
 
662
  if audio_clip is None:
663
  raise ValueError("์˜ค๋””์˜ค๋ฅผ ๋กœ๋“œํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")
@@ -680,38 +725,59 @@ def merge_videos_with_audio(video_files, audio_file, audio_volume, output_fps):
680
  looped_audio = concatenate_audioclips(audio_clips_list)
681
  audio_clip = looped_audio.subclip(0, video_duration)
682
 
683
- # ๊ธฐ์กด ์˜ค๋””์˜ค์™€ ์ƒˆ ์˜ค๋””์˜ค ํ•ฉ์„ฑ (๊ธฐ์กด ์˜ค๋””์˜ค๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ)
684
- if final_video.audio:
685
- final_audio = CompositeAudioClip([final_video.audio, audio_clip])
686
- final_video = final_video.set_audio(final_audio)
687
- else:
688
- final_video = final_video.set_audio(audio_clip)
 
 
 
 
 
 
689
 
690
  except Exception as e:
691
- logging.warning(f"์˜ค๋””์˜ค ์ฒ˜๋ฆฌ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}")
692
  # ์˜ค๋””์˜ค ์ฒ˜๋ฆฌ ์‹คํŒจํ•ด๋„ ๋น„๋””์˜ค๋Š” ๊ณ„์† ์ฒ˜๋ฆฌ
 
693
 
694
  status = "๋น„๋””์˜ค ์ €์žฅ ์ค‘..."
695
 
696
  # ์ž„์‹œ ํŒŒ์ผ๋กœ ์ €์žฅ
697
  with tempfile.NamedTemporaryFile(suffix=".mp4", delete=False) as temp_file:
698
  temp_filepath = temp_file.name
 
 
699
  final_video.write_videofile(
700
  temp_filepath,
701
  fps=output_fps,
702
  codec="libx264",
703
- audio_codec="aac"
 
 
 
704
  )
705
 
706
  # ๋ฆฌ์†Œ์Šค ์ •๋ฆฌ
707
  for clip in video_clips:
708
  clip.close()
 
 
 
 
 
 
 
709
  final_video.close()
710
 
711
- return temp_filepath, f"โœ… ์„ฑ๊ณต์ ์œผ๋กœ {len(video_paths)}๊ฐœ์˜ ๋น„๋””์˜ค๋ฅผ ๋ณ‘ํ•ฉํ–ˆ์Šต๋‹ˆ๋‹ค!"
712
 
713
  except Exception as e:
714
  logging.error(f"Video merge error: {str(e)}")
 
 
715
  return None, f"โŒ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}"
716
 
717
  # CSS
@@ -955,9 +1021,10 @@ with demo:
955
 
956
  with gr.Group(elem_classes="panel-box"):
957
  gr.Markdown("### ๐ŸŽต ์˜ค๋””์˜ค ์„ค์ • (์„ ํƒ)")
 
958
 
959
  audio_file = gr.Audio(
960
- label="์˜ค๋””์˜ค ํŒŒ์ผ",
961
  type="filepath",
962
  sources=["upload"]
963
  )
@@ -971,6 +1038,12 @@ with demo:
971
  info="100% = ์›๋ณธ ๋ณผ๋ฅจ"
972
  )
973
 
 
 
 
 
 
 
974
  with gr.Group(elem_classes="panel-box"):
975
  gr.Markdown("### โš™๏ธ ํŽธ์ง‘ ์„ค์ •")
976
 
@@ -982,6 +1055,13 @@ with demo:
982
  label="์ถœ๋ ฅ FPS (0 = ์ฒซ ๋ฒˆ์งธ ๋น„๋””์˜ค์˜ FPS ์‚ฌ์šฉ)"
983
  )
984
 
 
 
 
 
 
 
 
985
  merge_videos_btn = gr.Button("๐ŸŽฌ ๋น„๋””์˜ค ๋ณ‘ํ•ฉ", variant="primary", elem_id="merge-btn")
986
 
987
  # ์ถœ๋ ฅ ์ปฌ๋Ÿผ
@@ -999,7 +1079,14 @@ with demo:
999
  3. (์„ ํƒ) ์˜ค๋””์˜ค ํŒŒ์ผ์„ ์ถ”๊ฐ€ํ•˜๊ณ  ๋ณผ๋ฅจ์„ ์กฐ์ ˆํ•˜์„ธ์š”
1000
  4. '๋น„๋””์˜ค ๋ณ‘ํ•ฉ' ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์„ธ์š”
1001
 
1002
- **ํŒ**: ํŒŒ์ผ๋ช…์„ 01.mp4, 02.mp4, 03.mp4 ํ˜•์‹์œผ๋กœ ์ง€์ •ํ•˜๋ฉด ์ˆœ์„œ ๊ด€๋ฆฌ๊ฐ€ ์‰ฝ์Šต๋‹ˆ๋‹ค.
 
 
 
 
 
 
 
1003
  """)
1004
 
1005
  # ๋‹ค์„ฏ ๋ฒˆ์งธ ํƒญ: ๋น„๋””์˜ค ๋ฐฐ๊ฒฝ์ œ๊ฑฐ/ํ•ฉ์„ฑ
 
35
  from transformers import AutoModelForImageSegmentation
36
  from torchvision import transforms
37
  from moviepy import VideoFileClip, vfx, concatenate_videoclips, ImageSequenceClip, concatenate_audioclips, AudioFileClip, CompositeAudioClip
38
+ from moviepy.video.compositing.CompositeVideoClip import CompositeVideoClip
39
+ from moviepy.video.VideoClip import ColorClip
40
  import time
41
  from concurrent.futures import ThreadPoolExecutor
42
 
 
630
 
631
  status = f"{len(video_paths)}๊ฐœ์˜ ๋น„๋””์˜ค ๋กœ๋“œ ์ค‘..."
632
 
633
+ # ๋น„๋””์˜ค ํด๋ฆฝ ๋กœ๋“œ ๋ฐ ํฌ๊ธฐ ํ™•์ธ
634
  video_clips = []
635
+ max_width = 0
636
+ max_height = 0
637
+
638
  for i, video_path in enumerate(video_paths):
639
  status = f"๋น„๋””์˜ค {i+1}/{len(video_paths)} ๋กœ๋“œ ์ค‘: {os.path.basename(video_path)}"
640
  clip = VideoFileClip(video_path)
641
  video_clips.append(clip)
642
+
643
+ # ์ตœ๋Œ€ ํฌ๊ธฐ ์ถ”์ 
644
+ if clip.w > max_width:
645
+ max_width = clip.w
646
+ if clip.h > max_height:
647
+ max_height = clip.h
648
+
649
+ # ์ž„์‹œ ๋ณ€์ˆ˜ ์ •๋ฆฌ๋ฅผ ์œ„ํ•œ ๋ฐฐ๊ฒฝ ํด๋ฆฝ ์ €์žฅ
650
+ background_clips = []
651
+
652
+ # ๋ชจ๋“  ๋น„๋””์˜ค๋ฅผ ์ตœ๋Œ€ ํฌ๊ธฐ๋กœ ์กฐ์ • (์›๋ณธ ํ’ˆ์งˆ ์œ ์ง€)
653
+ resized_clips = []
654
+ for clip in video_clips:
655
+ if clip.w != max_width or clip.h != max_height:
656
+ # ํŒจ๋”ฉ ์ถ”๊ฐ€ ๋ฐฉ์‹ (์›๋ณธ ํ’ˆ์งˆ ์œ ์ง€)
657
+ # ๊ฒ€์€์ƒ‰ ๋ฐฐ๊ฒฝ ์ƒ์„ฑ
658
+ background = ColorClip(size=(max_width, max_height), color=(0,0,0), duration=clip.duration)
659
+ background_clips.append(background)
660
+
661
+ # ํด๋ฆฝ์„ ์ค‘์•™์— ๋ฐฐ์น˜
662
+ x_center = (max_width - clip.w) // 2
663
+ y_center = (max_height - clip.h) // 2
664
+
665
+ resized_clip = CompositeVideoClip([
666
+ background,
667
+ clip.set_position((x_center, y_center))
668
+ ])
669
+
670
+ resized_clips.append(resized_clip)
671
+ else:
672
+ resized_clips.append(clip)
673
 
674
  # ์ฒซ ๋ฒˆ์งธ ๋น„๋””์˜ค์˜ FPS๋ฅผ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์‚ฌ์šฉ
675
  if output_fps == 0:
 
678
  status = "๋น„๋””์˜ค ๋ณ‘ํ•ฉ ์ค‘..."
679
 
680
  # ๋น„๋””์˜ค ๋ณ‘ํ•ฉ
681
+ final_video = concatenate_videoclips(resized_clips, method="compose")
682
 
683
  # ์˜ค๋””์˜ค ์ฒ˜๋ฆฌ
684
  if audio_file:
685
  status = "์˜ค๋””์˜ค ์ฒ˜๋ฆฌ ์ค‘..."
686
 
687
  try:
688
+ # ์˜ค๋””์˜ค ํŒŒ์ผ ๊ฒฝ๋กœ ํ™•์ธ
689
+ if isinstance(audio_file, str):
690
+ audio_path = audio_file
691
+ else:
692
+ # gr.Audio์—์„œ ๋ฐ˜ํ™˜๋œ ํŠœํ”Œ์ธ ๊ฒฝ์šฐ
693
+ audio_path = audio_file
694
+
695
+ logging.info(f"Processing audio from: {audio_path}")
696
+
697
  # ์˜ค๋””์˜ค ๋กœ๋“œ
698
+ if audio_path.endswith(('.mp4', '.avi', '.mov', '.mkv')):
699
  # ๋น„๋””์˜ค ํŒŒ์ผ์—์„œ ์˜ค๋””์˜ค ์ถ”์ถœ
700
+ temp_video = VideoFileClip(audio_path)
701
  audio_clip = temp_video.audio
702
  temp_video.close()
703
  else:
704
  # ์˜ค๋””์˜ค ํŒŒ์ผ ์ง์ ‘ ๋กœ๋“œ
705
+ audio_clip = AudioFileClip(audio_path)
706
 
707
  if audio_clip is None:
708
  raise ValueError("์˜ค๋””์˜ค๋ฅผ ๋กœ๋“œํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")
 
725
  looped_audio = concatenate_audioclips(audio_clips_list)
726
  audio_clip = looped_audio.subclip(0, video_duration)
727
 
728
+ # ๊ธฐ์กด ์˜ค๋””์˜ค ์ œ๊ฑฐํ•˜๊ณ  ์ƒˆ ์˜ค๋””์˜ค๋กœ ๊ต์ฒด
729
+ # (๊ธฐ์กด ์˜ค๋””์˜ค์™€ ํ•ฉ์„ฑํ•˜๋ ค๋ฉด ์•„๋ž˜ ์ฃผ์„ ํ•ด์ œ)
730
+ final_video = final_video.set_audio(audio_clip)
731
+
732
+ # ๊ธฐ์กด ์˜ค๋””์˜ค์™€ ์ƒˆ ์˜ค๋””์˜ค ํ•ฉ์„ฑ์„ ์›ํ•˜๋Š” ๊ฒฝ์šฐ:
733
+ # if final_video.audio:
734
+ # final_audio = CompositeAudioClip([final_video.audio, audio_clip])
735
+ # final_video = final_video.set_audio(final_audio)
736
+ # else:
737
+ # final_video = final_video.set_audio(audio_clip)
738
+
739
+ logging.info("Audio successfully added to video")
740
 
741
  except Exception as e:
742
+ logging.error(f"์˜ค๋””์˜ค ์ฒ˜๋ฆฌ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}")
743
  # ์˜ค๋””์˜ค ์ฒ˜๋ฆฌ ์‹คํŒจํ•ด๋„ ๋น„๋””์˜ค๋Š” ๊ณ„์† ์ฒ˜๋ฆฌ
744
+ status = f"์˜ค๋””์˜ค ์ฒ˜๋ฆฌ ์‹คํŒจ: {str(e)}, ๋น„๋””์˜ค๋งŒ ๋ณ‘ํ•ฉํ•ฉ๋‹ˆ๋‹ค."
745
 
746
  status = "๋น„๋””์˜ค ์ €์žฅ ์ค‘..."
747
 
748
  # ์ž„์‹œ ํŒŒ์ผ๋กœ ์ €์žฅ
749
  with tempfile.NamedTemporaryFile(suffix=".mp4", delete=False) as temp_file:
750
  temp_filepath = temp_file.name
751
+
752
+ # ์ฝ”๋ฑ ์„ค์ • - ์›๋ณธ ํ’ˆ์งˆ ์œ ์ง€
753
  final_video.write_videofile(
754
  temp_filepath,
755
  fps=output_fps,
756
  codec="libx264",
757
+ audio_codec="aac",
758
+ preset="medium", # ํ’ˆ์งˆ ์„ค์ •
759
+ bitrate="5000k", # ๋น„ํŠธ๋ ˆ์ดํŠธ ์„ค์ •์œผ๋กœ ํ’ˆ์งˆ ์œ ์ง€
760
+ audio_bitrate="192k"
761
  )
762
 
763
  # ๋ฆฌ์†Œ์Šค ์ •๋ฆฌ
764
  for clip in video_clips:
765
  clip.close()
766
+ for clip in background_clips:
767
+ clip.close()
768
+ for i, clip in enumerate(resized_clips):
769
+ if clip != video_clips[i] if i < len(video_clips) else True: # ๋ฆฌ์‚ฌ์ด์ฆˆ๋œ ๊ฒฝ์šฐ๋งŒ
770
+ clip.close()
771
+ if audio_file and 'audio_clip' in locals():
772
+ audio_clip.close()
773
  final_video.close()
774
 
775
+ return temp_filepath, f"โœ… ์„ฑ๊ณต์ ์œผ๋กœ {len(video_paths)}๊ฐœ์˜ ๋น„๋””์˜ค๋ฅผ ๋ณ‘ํ•ฉํ–ˆ์Šต๋‹ˆ๋‹ค! (ํฌ๊ธฐ: {max_width}x{max_height})"
776
 
777
  except Exception as e:
778
  logging.error(f"Video merge error: {str(e)}")
779
+ import traceback
780
+ traceback.print_exc()
781
  return None, f"โŒ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}"
782
 
783
  # CSS
 
1021
 
1022
  with gr.Group(elem_classes="panel-box"):
1023
  gr.Markdown("### ๐ŸŽต ์˜ค๋””์˜ค ์„ค์ • (์„ ํƒ)")
1024
+ gr.Markdown("**์ฃผ์˜**: ์—…๋กœ๋“œํ•œ ์˜ค๋””์˜ค๊ฐ€ ๋น„๋””์˜ค์˜ ๊ธฐ์กด ์˜ค๋””์˜ค๋ฅผ ์™„์ „ํžˆ ๋Œ€์ฒดํ•ฉ๋‹ˆ๋‹ค.")
1025
 
1026
  audio_file = gr.Audio(
1027
+ label="์˜ค๋””์˜ค ํŒŒ์ผ (MP3, WAV, M4A ๋“ฑ)",
1028
  type="filepath",
1029
  sources=["upload"]
1030
  )
 
1038
  info="100% = ์›๋ณธ ๋ณผ๋ฅจ"
1039
  )
1040
 
1041
+ gr.Markdown("""
1042
+ **์˜ค๋””์˜ค ์˜ต์…˜**:
1043
+ - ์˜ค๋””์˜ค๊ฐ€ ๋น„๋””์˜ค๋ณด๋‹ค ์งง์œผ๋ฉด ์ž๋™์œผ๋กœ ๋ฐ˜๋ณต๋ฉ๋‹ˆ๋‹ค
1044
+ - ์˜ค๋””์˜ค๊ฐ€ ๋น„๋””์˜ค๋ณด๋‹ค ๊ธธ๋ฉด ๋น„๋””์˜ค ๊ธธ์ด์— ๋งž์ถฐ ์ž˜๋ฆฝ๋‹ˆ๋‹ค
1045
+ """)
1046
+
1047
  with gr.Group(elem_classes="panel-box"):
1048
  gr.Markdown("### โš™๏ธ ํŽธ์ง‘ ์„ค์ •")
1049
 
 
1055
  label="์ถœ๋ ฅ FPS (0 = ์ฒซ ๋ฒˆ์งธ ๋น„๋””์˜ค์˜ FPS ์‚ฌ์šฉ)"
1056
  )
1057
 
1058
+ gr.Markdown("""
1059
+ **ํฌ๊ธฐ ์ฒ˜๋ฆฌ**:
1060
+ - ๋ชจ๋“  ๋น„๋””์˜ค๋Š” ๊ฐ€์žฅ ํฐ ๋น„๋””์˜ค์˜ ํฌ๊ธฐ์— ๋งž์ถฐ์ง‘๋‹ˆ๋‹ค
1061
+ - ์ž‘์€ ๋น„๋””์˜ค๋Š” ๊ฒ€์€์ƒ‰ ํŒจ๋”ฉ์ด ์ถ”๊ฐ€๋˜์–ด ์ค‘์•™์— ๋ฐฐ์น˜๋ฉ๋‹ˆ๋‹ค
1062
+ - ์›๋ณธ ํ™”์งˆ์ด ๊ทธ๋Œ€๋กœ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค
1063
+ """)
1064
+
1065
  merge_videos_btn = gr.Button("๐ŸŽฌ ๋น„๋””์˜ค ๋ณ‘ํ•ฉ", variant="primary", elem_id="merge-btn")
1066
 
1067
  # ์ถœ๋ ฅ ์ปฌ๋Ÿผ
 
1079
  3. (์„ ํƒ) ์˜ค๋””์˜ค ํŒŒ์ผ์„ ์ถ”๊ฐ€ํ•˜๊ณ  ๋ณผ๋ฅจ์„ ์กฐ์ ˆํ•˜์„ธ์š”
1080
  4. '๋น„๋””์˜ค ๋ณ‘ํ•ฉ' ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์„ธ์š”
1081
 
1082
+ **ํŠน์ง•**:
1083
+ - โœ… ์ž‘์€ ๋น„๋””์˜ค๋Š” ํŒจ๋”ฉ ์ถ”๊ฐ€๋กœ ์›๋ณธ ํ™”์งˆ ์œ ์ง€
1084
+ - โœ… ์—…๋กœ๋“œํ•œ ์˜ค๋””์˜ค๊ฐ€ ์ „์ฒด ๋น„๋””์˜ค์— ์ ์šฉ๋ฉ๋‹ˆ๋‹ค
1085
+ - โœ… ์›๋ณธ ํ™”์งˆ์ด ์ตœ๋Œ€ํ•œ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค
1086
+
1087
+ **ํŒ**:
1088
+ - ํŒŒ์ผ๋ช…์„ 01.mp4, 02.mp4, 03.mp4 ํ˜•์‹์œผ๋กœ ์ง€์ •ํ•˜๋ฉด ์ˆœ์„œ ๊ด€๋ฆฌ๊ฐ€ ์‰ฝ์Šต๋‹ˆ๋‹ค
1089
+ - ์˜ค๋””์˜ค๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด ๊ธฐ์กด ๋น„๋””์˜ค์˜ ์˜ค๋””์˜ค๋Š” ๋Œ€์ฒด๋ฉ๋‹ˆ๋‹ค
1090
  """)
1091
 
1092
  # ๋‹ค์„ฏ ๋ฒˆ์งธ ํƒญ: ๋น„๋””์˜ค ๋ฐฐ๊ฒฝ์ œ๊ฑฐ/ํ•ฉ์„ฑ