openfree commited on
Commit
0f9eae9
ยท
verified ยท
1 Parent(s): 8a835ac

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +176 -4
app.py CHANGED
@@ -19,7 +19,7 @@ from transformers import pipeline
19
  # ๋น„๋””์˜ค ๋ฐฐ๊ฒฝ์ œ๊ฑฐ๋ฅผ ์œ„ํ•œ ์ถ”๊ฐ€ import
20
  from transformers import AutoModelForImageSegmentation
21
  from torchvision import transforms
22
- from moviepy import VideoFileClip, vfx, concatenate_videoclips, ImageSequenceClip
23
  import time
24
  from concurrent.futures import ThreadPoolExecutor
25
 
@@ -564,6 +564,106 @@ def process_video_bg(vid, bg_type="์ƒ‰์ƒ", bg_image=None, bg_video=None, color=
564
  yield gr.update(visible=False), gr.update(visible=True), f"๋น„๋””์˜ค ์ฒ˜๋ฆฌ ์˜ค๋ฅ˜: {e}. ๊ฒฝ๊ณผ ์‹œ๊ฐ„: {elapsed_time:.2f}์ดˆ"
565
  yield None, f"๋น„๋””์˜ค ์ฒ˜๋ฆฌ ์˜ค๋ฅ˜: {e}", f"๋น„๋””์˜ค ์ฒ˜๋ฆฌ ์˜ค๋ฅ˜: {e}. ๊ฒฝ๊ณผ ์‹œ๊ฐ„: {elapsed_time:.2f}์ดˆ"
566
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
567
  # CSS
568
  css = """
569
  :root {
@@ -589,7 +689,7 @@ css = """
589
  padding: 20px !important;
590
  margin-bottom: 20px !important;
591
  }
592
- #generate-btn, #video-btn, #outpaint-btn, #preview-btn, #audio-btn, #bg-remove-btn {
593
  background: linear-gradient(135deg, #ff9a9e, #fad0c4) !important;
594
  font-size: 1.1rem !important;
595
  padding: 12px 24px !important;
@@ -786,7 +886,72 @@ with demo:
786
  if not MMAUDIO_MODEL_LOADED:
787
  gr.Markdown("โš ๏ธ MMAudio ๋ชจ๋ธ์„ ๋กœ๋“œํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด ๊ธฐ๋Šฅ์€ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")
788
 
789
- # ๋„ค ๋ฒˆ์งธ ํƒญ: ๋น„๋””์˜ค ๋ฐฐ๊ฒฝ์ œ๊ฑฐ/ํ•ฉ์„ฑ
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
790
  with gr.Tab("๋น„๋””์˜ค ๋ฐฐ๊ฒฝ์ œ๊ฑฐ/ํ•ฉ์„ฑ", elem_classes="tabitem"):
791
  with gr.Row(equal_height=True):
792
  # ์ž…๋ ฅ ์ปฌ๋Ÿผ
@@ -927,7 +1092,14 @@ with demo:
927
  [output_video_with_audio]
928
  )
929
 
930
- # ์ด๋ฒคํŠธ ์—ฐ๊ฒฐ - ๋„ค ๋ฒˆ์งธ ํƒญ
 
 
 
 
 
 
 
931
  def update_bg_visibility(bg_type):
932
  if bg_type == "์ƒ‰์ƒ":
933
  return gr.update(visible=True), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False)
 
19
  # ๋น„๋””์˜ค ๋ฐฐ๊ฒฝ์ œ๊ฑฐ๋ฅผ ์œ„ํ•œ ์ถ”๊ฐ€ import
20
  from transformers import AutoModelForImageSegmentation
21
  from torchvision import transforms
22
+ from moviepy import VideoFileClip, vfx, concatenate_videoclips, ImageSequenceClip, concatenate_audioclips, AudioFileClip, CompositeAudioClip
23
  import time
24
  from concurrent.futures import ThreadPoolExecutor
25
 
 
564
  yield gr.update(visible=False), gr.update(visible=True), f"๋น„๋””์˜ค ์ฒ˜๋ฆฌ ์˜ค๋ฅ˜: {e}. ๊ฒฝ๊ณผ ์‹œ๊ฐ„: {elapsed_time:.2f}์ดˆ"
565
  yield None, f"๋น„๋””์˜ค ์ฒ˜๋ฆฌ ์˜ค๋ฅ˜: {e}", f"๋น„๋””์˜ค ์ฒ˜๋ฆฌ ์˜ค๋ฅ˜: {e}. ๊ฒฝ๊ณผ ์‹œ๊ฐ„: {elapsed_time:.2f}์ดˆ"
566
 
567
+ @spaces.GPU
568
+ def merge_videos_with_audio(video_files, audio_file, audio_volume, output_fps):
569
+ """์—ฌ๋Ÿฌ ๋น„๋””์˜ค๋ฅผ ๋ณ‘ํ•ฉํ•˜๊ณ  ์˜ค๋””์˜ค๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ํ•จ์ˆ˜"""
570
+ if not video_files:
571
+ return None, "๋น„๋””์˜ค ํŒŒ์ผ์„ ์—…๋กœ๋“œํ•ด์ฃผ์„ธ์š”."
572
+
573
+ if len(video_files) > 10:
574
+ return None, "์ตœ๋Œ€ 10๊ฐœ์˜ ๋น„๋””์˜ค๋งŒ ์—…๋กœ๋“œ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค."
575
+
576
+ try:
577
+ # ์ƒํƒœ ์—…๋ฐ์ดํŠธ
578
+ status = "๋น„๋””์˜ค ํŒŒ์ผ ์ •๋ ฌ ์ค‘..."
579
+
580
+ # ํŒŒ์ผ ๊ฒฝ๋กœ์™€ ํŒŒ์ผ๋ช…์„ ํŠœํ”Œ๋กœ ์ €์žฅํ•˜๊ณ  ํŒŒ์ผ๋ช…์œผ๋กœ ์ •๋ ฌ
581
+ video_paths = []
582
+ if isinstance(video_files, list):
583
+ for video_file in video_files:
584
+ if video_file is not None:
585
+ video_paths.append(video_file)
586
+ else:
587
+ video_paths.append(video_files)
588
+
589
+ # ํŒŒ์ผ๋ช…์œผ๋กœ ์ •๋ ฌ (๊ฒฝ๋กœ์—์„œ ํŒŒ์ผ๋ช…๋งŒ ์ถ”์ถœํ•˜์—ฌ ์ •๋ ฌ)
590
+ video_paths.sort(key=lambda x: os.path.basename(x))
591
+
592
+ status = f"{len(video_paths)}๊ฐœ์˜ ๋น„๋””์˜ค ๋กœ๋“œ ์ค‘..."
593
+
594
+ # ๋น„๋””์˜ค ํด๋ฆฝ ๋กœ๋“œ
595
+ video_clips = []
596
+ for i, video_path in enumerate(video_paths):
597
+ status = f"๋น„๋””์˜ค {i+1}/{len(video_paths)} ๋กœ๋“œ ์ค‘: {os.path.basename(video_path)}"
598
+ clip = VideoFileClip(video_path)
599
+ video_clips.append(clip)
600
+
601
+ # ์ฒซ ๋ฒˆ์งธ ๋น„๋””์˜ค์˜ FPS๋ฅผ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์‚ฌ์šฉ
602
+ if output_fps == 0:
603
+ output_fps = video_clips[0].fps
604
+
605
+ status = "๋น„๋””์˜ค ๋ณ‘ํ•ฉ ์ค‘..."
606
+
607
+ # ๋น„๋””์˜ค ๋ณ‘ํ•ฉ
608
+ final_video = concatenate_videoclips(video_clips, method="compose")
609
+
610
+ # ์˜ค๋””์˜ค ์ฒ˜๋ฆฌ
611
+ if audio_file:
612
+ status = "์˜ค๋””์˜ค ์ฒ˜๋ฆฌ ์ค‘..."
613
+
614
+ # ์˜ค๋””์˜ค ๋กœ๋“œ
615
+ audio_clip = VideoFileClip(audio_file).audio if audio_file.endswith(('.mp4', '.avi', '.mov')) else None
616
+
617
+ if audio_clip is None:
618
+ # ์˜ค๋””์˜ค ํŒŒ์ผ์ธ ๊ฒฝ์šฐ
619
+ audio_clip = AudioFileClip(audio_file)
620
+
621
+ # ๋ณผ๋ฅจ ์กฐ์ ˆ
622
+ if audio_volume != 100:
623
+ audio_clip = audio_clip.volumex(audio_volume / 100)
624
+
625
+ # ์˜ค๋””์˜ค๋ฅผ ๋น„๋””์˜ค ๊ธธ์ด์— ๋งž์ถค
626
+ video_duration = final_video.duration
627
+ audio_duration = audio_clip.duration
628
+
629
+ if audio_duration > video_duration:
630
+ # ์˜ค๋””์˜ค๊ฐ€ ๋” ๊ธธ๋ฉด ์ž˜๋ผ๋ƒ„
631
+ audio_clip = audio_clip.subclip(0, video_duration)
632
+ elif audio_duration < video_duration:
633
+ # ์˜ค๋””์˜ค๊ฐ€ ๋” ์งง์œผ๋ฉด ๋ฐ˜๋ณต
634
+ loops_needed = int(video_duration / audio_duration) + 1
635
+ audio_clip = concatenate_audioclips([audio_clip] * loops_needed).subclip(0, video_duration)
636
+
637
+ # ๊ธฐ์กด ์˜ค๋””์˜ค์™€ ์ƒˆ ์˜ค๋””์˜ค ํ•ฉ์„ฑ (๊ธฐ์กด ์˜ค๋””์˜ค๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ)
638
+ if final_video.audio:
639
+ final_audio = CompositeAudioClip([final_video.audio, audio_clip])
640
+ final_video = final_video.set_audio(final_audio)
641
+ else:
642
+ final_video = final_video.set_audio(audio_clip)
643
+
644
+ status = "๋น„๋””์˜ค ์ €์žฅ ์ค‘..."
645
+
646
+ # ์ž„์‹œ ํŒŒ์ผ๋กœ ์ €์žฅ
647
+ with tempfile.NamedTemporaryFile(suffix=".mp4", delete=False) as temp_file:
648
+ temp_filepath = temp_file.name
649
+ final_video.write_videofile(
650
+ temp_filepath,
651
+ fps=output_fps,
652
+ codec="libx264",
653
+ audio_codec="aac"
654
+ )
655
+
656
+ # ๋ฆฌ์†Œ์Šค ์ •๋ฆฌ
657
+ for clip in video_clips:
658
+ clip.close()
659
+ final_video.close()
660
+
661
+ return temp_filepath, f"โœ… ์„ฑ๊ณต์ ์œผ๋กœ {len(video_paths)}๊ฐœ์˜ ๋น„๋””์˜ค๋ฅผ ๋ณ‘ํ•ฉํ–ˆ์Šต๋‹ˆ๋‹ค!"
662
+
663
+ except Exception as e:
664
+ logging.error(f"Video merge error: {str(e)}")
665
+ return None, f"โŒ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}"
666
+
667
  # CSS
668
  css = """
669
  :root {
 
689
  padding: 20px !important;
690
  margin-bottom: 20px !important;
691
  }
692
+ #generate-btn, #video-btn, #outpaint-btn, #preview-btn, #audio-btn, #bg-remove-btn, #merge-btn {
693
  background: linear-gradient(135deg, #ff9a9e, #fad0c4) !important;
694
  font-size: 1.1rem !important;
695
  padding: 12px 24px !important;
 
886
  if not MMAUDIO_MODEL_LOADED:
887
  gr.Markdown("โš ๏ธ MMAudio ๋ชจ๋ธ์„ ๋กœ๋“œํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด ๊ธฐ๋Šฅ์€ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")
888
 
889
+ # ๋„ค ๋ฒˆ์งธ ํƒญ: ๋น„๋””์˜ค ํŽธ์ง‘
890
+ with gr.Tab("๋น„๋””์˜ค ํŽธ์ง‘", elem_classes="tabitem"):
891
+ with gr.Row(equal_height=True):
892
+ # ์ž…๋ ฅ ์ปฌ๋Ÿผ
893
+ with gr.Column(scale=1):
894
+ with gr.Group(elem_classes="panel-box"):
895
+ gr.Markdown("### ๐ŸŽฅ ๋น„๋””์˜ค ์—…๋กœ๋“œ (์ตœ๋Œ€ 10๊ฐœ)")
896
+ gr.Markdown("**ํŒŒ์ผ๋ช…์ด ์ž‘์„์ˆ˜๋ก ์šฐ์„ ์ˆœ์œ„๊ฐ€ ๋†’์Šต๋‹ˆ๋‹ค** (์˜ˆ: 1.mp4, 2.mp4, 3.mp4)")
897
+
898
+ video_files = gr.File(
899
+ label="๋น„๋””์˜ค ํŒŒ์ผ๋“ค",
900
+ file_count="multiple",
901
+ file_types=["video"],
902
+ type="filepath"
903
+ )
904
+
905
+ with gr.Group(elem_classes="panel-box"):
906
+ gr.Markdown("### ๐ŸŽต ์˜ค๋””์˜ค ์„ค์ • (์„ ํƒ)")
907
+
908
+ audio_file = gr.Audio(
909
+ label="์˜ค๋””์˜ค ํŒŒ์ผ",
910
+ type="filepath",
911
+ sources=["upload"]
912
+ )
913
+
914
+ audio_volume = gr.Slider(
915
+ minimum=0,
916
+ maximum=200,
917
+ value=100,
918
+ step=1,
919
+ label="์˜ค๋””์˜ค ๋ณผ๋ฅจ (%)",
920
+ info="100% = ์›๋ณธ ๋ณผ๋ฅจ"
921
+ )
922
+
923
+ with gr.Group(elem_classes="panel-box"):
924
+ gr.Markdown("### โš™๏ธ ํŽธ์ง‘ ์„ค์ •")
925
+
926
+ output_fps = gr.Slider(
927
+ minimum=0,
928
+ maximum=60,
929
+ value=0,
930
+ step=1,
931
+ label="์ถœ๋ ฅ FPS (0 = ์ฒซ ๋ฒˆ์งธ ๋น„๋””์˜ค์˜ FPS ์‚ฌ์šฉ)"
932
+ )
933
+
934
+ merge_videos_btn = gr.Button("๐ŸŽฌ ๋น„๋””์˜ค ๋ณ‘ํ•ฉ", variant="primary", elem_id="merge-btn")
935
+
936
+ # ์ถœ๋ ฅ ์ปฌ๋Ÿผ
937
+ with gr.Column(scale=1):
938
+ with gr.Group(elem_classes="panel-box"):
939
+ gr.Markdown("### ๐ŸŽฌ ๋ณ‘ํ•ฉ ๊ฒฐ๊ณผ")
940
+
941
+ merge_status = gr.Textbox(label="์ฒ˜๋ฆฌ ์ƒํƒœ", interactive=False)
942
+ merged_video = gr.Video(label="๋ณ‘ํ•ฉ๋œ ๋น„๋””์˜ค")
943
+
944
+ gr.Markdown("""
945
+ ### โ„น๏ธ ์‚ฌ์šฉ ๋ฐฉ๋ฒ•
946
+ 1. ์—ฌ๋Ÿฌ ๋น„๋””์˜ค ํŒŒ์ผ์„ ์—…๋กœ๋“œํ•˜์„ธ์š” (์ตœ๋Œ€ 10๊ฐœ)
947
+ 2. ํŒŒ์ผ๋ช…์ด ์ž‘์€ ์ˆœ์„œ๋Œ€๋กœ ์ž๋™ ์ •๋ ฌ๋ฉ๋‹ˆ๋‹ค
948
+ 3. (์„ ํƒ) ์˜ค๋””์˜ค ํŒŒ์ผ์„ ์ถ”๊ฐ€ํ•˜๊ณ  ๋ณผ๋ฅจ์„ ์กฐ์ ˆํ•˜์„ธ์š”
949
+ 4. '๋น„๋””์˜ค ๋ณ‘ํ•ฉ' ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์„ธ์š”
950
+
951
+ **ํŒ**: ํŒŒ์ผ๋ช…์„ 01.mp4, 02.mp4, 03.mp4 ํ˜•์‹์œผ๋กœ ์ง€์ •ํ•˜๋ฉด ์ˆœ์„œ ๊ด€๋ฆฌ๊ฐ€ ์‰ฝ์Šต๋‹ˆ๋‹ค.
952
+ """)
953
+
954
+ # ๋‹ค์„ฏ ๋ฒˆ์งธ ํƒญ: ๋น„๋””์˜ค ๋ฐฐ๊ฒฝ์ œ๊ฑฐ/ํ•ฉ์„ฑ
955
  with gr.Tab("๋น„๋””์˜ค ๋ฐฐ๊ฒฝ์ œ๊ฑฐ/ํ•ฉ์„ฑ", elem_classes="tabitem"):
956
  with gr.Row(equal_height=True):
957
  # ์ž…๋ ฅ ์ปฌ๋Ÿผ
 
1092
  [output_video_with_audio]
1093
  )
1094
 
1095
+ # ์ด๋ฒคํŠธ ์—ฐ๊ฒฐ - ๋„ค ๋ฒˆ์งธ ํƒญ (๋น„๋””์˜ค ํŽธ์ง‘)
1096
+ merge_videos_btn.click(
1097
+ merge_videos_with_audio,
1098
+ inputs=[video_files, audio_file, audio_volume, output_fps],
1099
+ outputs=[merged_video, merge_status]
1100
+ )
1101
+
1102
+ # ์ด๋ฒคํŠธ ์—ฐ๊ฒฐ - ๋‹ค์„ฏ ๋ฒˆ์งธ ํƒญ (๋น„๋””์˜ค ๋ฐฐ๊ฒฝ์ œ๊ฑฐ/ํ•ฉ์„ฑ)
1103
  def update_bg_visibility(bg_type):
1104
  if bg_type == "์ƒ‰์ƒ":
1105
  return gr.update(visible=True), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False)