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)
|