Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -1,122 +1,130 @@
|
|
1 |
import gradio as gr
|
2 |
import moviepy.editor as mp
|
|
|
3 |
import os
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
42 |
|
43 |
# 썸네일 생성
|
44 |
-
start_thumb =
|
45 |
-
end_thumb = trimmed_clip.get_frame(end_time - start_time)
|
46 |
-
start_thumbnail = Image.fromarray(start_thumb)
|
47 |
-
end_thumbnail = Image.fromarray(end_thumb)
|
48 |
-
|
49 |
-
# GIF 저장
|
50 |
-
gif_path = "output.gif"
|
51 |
-
trimmed_clip.write_gif(gif_path, loop=loop_count)
|
52 |
-
print("GIF 생성 완료:", gif_path)
|
53 |
-
|
54 |
-
return start_thumbnail, end_thumbnail, gif_path
|
55 |
|
|
|
|
|
56 |
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
# Gradio 인터페이스 구성
|
62 |
-
with gr.Blocks() as app:
|
63 |
-
gr.Markdown("## 동영상을 GIF로 변환하는 툴")
|
64 |
-
|
65 |
-
# 동영상 업로드
|
66 |
-
video_input = gr.Video(label="동영상 업로드")
|
67 |
|
68 |
-
|
69 |
-
|
70 |
-
video_info = gr.Label(label="동영상 정보")
|
71 |
|
72 |
-
|
73 |
-
|
74 |
-
end_time = gr.Textbox(label="끝 시간 (초 단위)", placeholder="예: 10.0")
|
75 |
|
76 |
-
|
77 |
-
|
78 |
-
end_thumbnail_output = gr.Image(label="끝 지점 썸네일")
|
79 |
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
value="원본",
|
84 |
-
label="해상도 선택"
|
85 |
-
)
|
86 |
|
87 |
-
|
88 |
-
|
|
|
89 |
|
90 |
-
|
91 |
-
|
|
|
92 |
|
93 |
-
|
94 |
-
|
|
|
95 |
|
96 |
-
# GIF 생성 버튼
|
97 |
generate_button = gr.Button("GIF 생성")
|
|
|
|
|
|
|
|
|
98 |
|
99 |
-
|
100 |
-
|
101 |
-
|
|
|
|
|
102 |
|
103 |
-
|
104 |
-
video_input.change(
|
105 |
-
lambda video_file: (video_file, {"동영상 길이(초)": mp.VideoFileClip(video_file.name).duration}),
|
106 |
-
inputs=[video_input],
|
107 |
-
outputs=[video_output, video_info]
|
108 |
-
)
|
109 |
|
110 |
generate_button.click(
|
111 |
-
process_video,
|
112 |
-
inputs=[
|
113 |
-
|
114 |
-
fps_slider, playback_speed_slider, loop_count_slider
|
115 |
-
],
|
116 |
-
outputs=[start_thumbnail_output, end_thumbnail_output, gif_download_output]
|
117 |
)
|
118 |
|
119 |
-
|
120 |
-
|
121 |
-
# 앱 실행
|
122 |
-
app.launch()
|
|
|
1 |
import gradio as gr
|
2 |
import moviepy.editor as mp
|
3 |
+
import imageio
|
4 |
import os
|
5 |
+
import logging
|
6 |
+
|
7 |
+
# 로그 설정
|
8 |
+
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
9 |
+
|
10 |
+
def get_video_info(video_path):
|
11 |
+
try:
|
12 |
+
clip = mp.VideoFileClip(video_path)
|
13 |
+
duration = clip.duration
|
14 |
+
logging.info(f"동영상 재생 시간: {duration}초")
|
15 |
+
return duration
|
16 |
+
except Exception as e:
|
17 |
+
logging.error(f"동영상 정보 추출 실패: {e}")
|
18 |
+
return None
|
19 |
+
|
20 |
+
def generate_thumbnails(video_path, start_time, end_time):
|
21 |
+
try:
|
22 |
+
clip = mp.VideoFileClip(video_path)
|
23 |
+
start_frame = clip.get_frame(start_time)
|
24 |
+
end_frame = clip.get_frame(end_time)
|
25 |
+
start_thumb_path = "start_thumbnail.png"
|
26 |
+
end_thumb_path = "end_thumbnail.png"
|
27 |
+
imageio.imwrite(start_thumb_path, start_frame)
|
28 |
+
imageio.imwrite(end_thumb_path, end_frame)
|
29 |
+
logging.info("썸네일 생성 성공")
|
30 |
+
return start_thumb_path, end_thumb_path
|
31 |
+
except Exception as e:
|
32 |
+
logging.error(f"썸네일 생성 실패: {e}")
|
33 |
+
return None, None
|
34 |
+
|
35 |
+
def create_gif(video_path, start_time, end_time, resolution, fps, speed, repeat):
|
36 |
+
try:
|
37 |
+
clip = mp.VideoFileClip(video_path).subclip(start_time, end_time)
|
38 |
+
# 해상도 조절
|
39 |
+
if resolution != 100:
|
40 |
+
new_width = int(clip.w * resolution / 100)
|
41 |
+
new_height = int(clip.h * resolution / 100)
|
42 |
+
clip = clip.resize(newsize=(new_width, new_height))
|
43 |
+
logging.info(f"해상도 조절: {new_width}x{new_height}")
|
44 |
+
# 프레임 속도 조절
|
45 |
+
clip = clip.set_fps(fps)
|
46 |
+
logging.info(f"프레임 속도 조절: {fps}fps")
|
47 |
+
# 재생 속도 조절
|
48 |
+
clip = clip.fx(mp.vfx.speedx, speed)
|
49 |
+
logging.info(f"재생 속도 조절: {speed}배속")
|
50 |
+
# GIF 반복 설정
|
51 |
+
if repeat == -1:
|
52 |
+
loop = 0 # 무한 반복
|
53 |
+
else:
|
54 |
+
loop = repeat
|
55 |
+
gif_path = "output.gif"
|
56 |
+
clip.write_gif(gif_path, loop=loop)
|
57 |
+
logging.info("GIF 생성 성공")
|
58 |
+
return gif_path
|
59 |
+
except Exception as e:
|
60 |
+
logging.error(f"GIF 생성 실패: {e}")
|
61 |
+
return None
|
62 |
+
|
63 |
+
def process_video(video, start_time, end_time, resolution, fps, speed, repeat):
|
64 |
+
if video is None:
|
65 |
+
logging.warning("업로드된 동영상이 없습니다.")
|
66 |
+
return None, None
|
67 |
+
|
68 |
+
video_path = video.name
|
69 |
+
logging.info(f"동영상 업로드: {video_path}")
|
70 |
+
|
71 |
+
# 동영상 정보 추출
|
72 |
+
duration = get_video_info(video_path)
|
73 |
|
74 |
# 썸네일 생성
|
75 |
+
start_thumb, end_thumb = generate_thumbnails(video_path, start_time, end_time)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
76 |
|
77 |
+
# GIF 생성
|
78 |
+
gif_path = create_gif(video_path, start_time, end_time, resolution, fps, speed, repeat)
|
79 |
|
80 |
+
if gif_path:
|
81 |
+
return gif_path, gif_path
|
82 |
+
else:
|
83 |
+
return None, None
|
|
|
|
|
|
|
|
|
|
|
|
|
84 |
|
85 |
+
with gr.Blocks() as demo:
|
86 |
+
gr.Markdown("# 동영상을 GIF로 변환하기")
|
|
|
87 |
|
88 |
+
with gr.Row():
|
89 |
+
video_input = gr.Video(label="동영상 업로드")
|
|
|
90 |
|
91 |
+
with gr.Row():
|
92 |
+
video_info = gr.Textbox(label="동영상 재생 시간 (초)", interactive=False)
|
|
|
93 |
|
94 |
+
with gr.Row():
|
95 |
+
start_time = gr.Number(label="시작 시간 (초)", value=0)
|
96 |
+
end_time = gr.Number(label="끝 시간 (초)", value=5)
|
|
|
|
|
|
|
97 |
|
98 |
+
with gr.Row():
|
99 |
+
start_thumbnail = gr.Image(label="시작 부분 썸네일")
|
100 |
+
end_thumbnail = gr.Image(label="끝 부분 썸네일")
|
101 |
|
102 |
+
with gr.Row():
|
103 |
+
resolution = gr.Slider(label="해상도 조절 (%)", minimum=10, maximum=200, step=10, value=100)
|
104 |
+
fps = gr.Slider(label="프레임 속도 조절", minimum=1, maximum=60, step=1, value=24)
|
105 |
|
106 |
+
with gr.Row():
|
107 |
+
speed = gr.Slider(label="재생 속도 조절", minimum=0.5, maximum=3.0, step=0.1, value=1.0)
|
108 |
+
repeat = gr.Slider(label="GIF 반복 횟수", minimum=-1, maximum=10, step=1, value=-1)
|
109 |
|
|
|
110 |
generate_button = gr.Button("GIF 생성")
|
111 |
+
|
112 |
+
with gr.Row():
|
113 |
+
gif_preview = gr.Image(label="GIF 미리보기")
|
114 |
+
gif_download = gr.File(label="GIF 다운로드")
|
115 |
|
116 |
+
def update_video_info(video):
|
117 |
+
if video is None:
|
118 |
+
return ""
|
119 |
+
duration = get_video_info(video.name)
|
120 |
+
return f"{duration:.2f} 초" if duration else "정보 불러오기 실패"
|
121 |
|
122 |
+
video_input.change(fn=update_video_info, inputs=video_input, outputs=video_info)
|
|
|
|
|
|
|
|
|
|
|
123 |
|
124 |
generate_button.click(
|
125 |
+
fn=process_video,
|
126 |
+
inputs=[video_input, start_time, end_time, resolution, fps, speed, repeat],
|
127 |
+
outputs=[gif_preview, gif_download]
|
|
|
|
|
|
|
128 |
)
|
129 |
|
130 |
+
demo.launch()
|
|
|
|
|
|