Update app.py
Browse files
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)
|