Kims12 commited on
Commit
2d0b48d
ยท
verified ยท
1 Parent(s): 4f86e7a

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +232 -0
app.py ADDED
@@ -0,0 +1,232 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import uuid
3
+ import moviepy.editor as mp
4
+ from PIL import Image
5
+ import gradio as gr
6
+
7
+ # ----------------------------------
8
+ # ๊ณตํ†ต์ ์œผ๋กœ ์‚ฌ์šฉํ•  ๋กœ๊ทธ ๋ฆฌ์ŠคํŠธ
9
+ # ----------------------------------
10
+ global_logs = []
11
+
12
+ def add_log(message: str):
13
+ """
14
+ ๋กœ๊ทธ ๋ฉ”์‹œ์ง€๋ฅผ ์ €์žฅํ•˜๋Š” ํ•จ์ˆ˜
15
+ """
16
+ global_logs.append(message)
17
+ print(message)
18
+
19
+ def parse_time_to_seconds(time_str: str):
20
+ """
21
+ ์‹œ:๋ถ„:์ดˆ ํ˜•ํƒœ์˜ ๋ฌธ์ž์—ด(์˜ˆ: '00:00:10' ๋˜๋Š” '00:01:02')์„ ์ดˆ ๋‹จ์œ„(float)๋กœ ๋ณ€ํ™˜
22
+ """
23
+ try:
24
+ h, m, s = time_str.split(":")
25
+ total_seconds = int(h) * 3600 + int(m) * 60 + float(s)
26
+ return total_seconds
27
+ except Exception:
28
+ return 0.0
29
+
30
+ def generate_thumbnail(video_clip, time_point):
31
+ """
32
+ ํŠน์ • ์‹œ์ ์˜ ํ”„๋ ˆ์ž„์„ ์ธ๋„ค์ผ๋กœ ์ƒ์„ฑํ•˜์—ฌ PIL.Image๋กœ ๋ฐ˜ํ™˜
33
+ """
34
+ try:
35
+ frame = video_clip.get_frame(time_point)
36
+ thumbnail_img = Image.fromarray(frame)
37
+ return thumbnail_img
38
+ except:
39
+ # ๋งŒ์•ฝ time_point๊ฐ€ ์žฌ์ƒ์‹œ๊ฐ„ ๋ฒ”์œ„๋ฅผ ๋ฒ—์–ด๋‚˜๋ฉด ์ฒซ ํ”„๋ ˆ์ž„์„ ์ธ๋„ค์ผ๋กœ
40
+ frame = video_clip.get_frame(0)
41
+ thumbnail_img = Image.fromarray(frame)
42
+ return thumbnail_img
43
+
44
+ def process_video(video,
45
+ start_time_str,
46
+ end_time_str,
47
+ resolution_factor,
48
+ frame_rate_factor,
49
+ speed_factor,
50
+ repeat_count):
51
+ """
52
+ ์ „์ฒด GIF ๋ณ€ํ™˜ ๊ณผ์ •์„ ์ˆ˜ํ–‰ํ•˜๋Š” ํ•จ์ˆ˜
53
+ """
54
+ global global_logs
55
+ global_logs = [] # ํ˜ธ์ถœ ์‹œ๋งˆ๋‹ค ๋กœ๊ทธ๋ฅผ ์ดˆ๊ธฐํ™”
56
+
57
+ # ๋กœ๊ทธ 1: ์—…๋กœ๋“œ๋œ ํŒŒ์ผ ํ™•์ธ
58
+ add_log("[LOG 1] ๋น„๋””์˜ค ์—…๋กœ๋“œ ๋ฐ ์ฒ˜๋ฆฌ ์‹œ์ž‘")
59
+
60
+ # ๋น„๋””์˜ค ๋ถˆ๋Ÿฌ์˜ค๊ธฐ
61
+ add_log("[LOG 2] ๋น„๋””์˜ค ๋กœ๋“œ ์ค‘...")
62
+ input_video = mp.VideoFileClip(video.name)
63
+
64
+ # ์žฌ์ƒ์‹œ๊ฐ„ ์ถœ๋ ฅ
65
+ duration = input_video.duration
66
+ add_log(f"[LOG 3] ์—…๋กœ๋“œ๋œ ์˜์ƒ์˜ ์žฌ์ƒ์‹œ๊ฐ„: {duration:.2f}์ดˆ")
67
+
68
+ # ์‹œ์ž‘/๋ ์‹œ๊ฐ„ parse
69
+ add_log("[LOG 4] ์‹œ์ž‘/๋ ์‹œ๊ฐ„ ํŒŒ์‹ฑ ์ค‘...")
70
+ start_sec = parse_time_to_seconds(start_time_str)
71
+ end_sec = parse_time_to_seconds(end_time_str)
72
+
73
+ # ์ž˜๋ชป๋œ ์ž…๋ ฅ ํ˜น์€ ๋ฒ”์œ„ ๋ฒ—์–ด๋‚˜๋ฉด ์ž๋™ ๋ณด์ •
74
+ if start_sec < 0:
75
+ start_sec = 0
76
+ if end_sec <= 0 or end_sec > duration:
77
+ end_sec = duration
78
+ if start_sec >= end_sec:
79
+ start_sec = 0
80
+ end_sec = duration
81
+
82
+ add_log(f"[LOG 5] ์ ์šฉ๋œ ์‹œ์ž‘ ์‹œ๊ฐ„: {start_sec}์ดˆ, ์ข…๋ฃŒ ์‹œ๊ฐ„: {end_sec}์ดˆ")
83
+
84
+ # ํ•ด๋‹น ๊ตฌ๊ฐ„ ์ž๋ฅด๊ธฐ(subclip)
85
+ add_log("[LOG 6] ์˜์ƒ ์ž๋ฅด๊ธฐ ์ž‘์—… ์ง„ํ–‰...")
86
+ clip = input_video.subclip(start_sec, end_sec)
87
+
88
+ # ์†๋„ ์กฐ์ ˆ
89
+ if abs(speed_factor - 1.0) > 1e-3:
90
+ add_log(f"[LOG 7] ์†๋„ {speed_factor}๋ฐฐ๋กœ ์กฐ์ ˆ ์ค‘...")
91
+ clip = clip.fx(mp.vfx.speedx, speed_factor)
92
+
93
+ # ํ•ด์ƒ๋„ ์กฐ์ ˆ (๊ธฐ๋ณธ 1.0 = ์›๋ณธ)
94
+ if abs(resolution_factor - 1.0) > 1e-3:
95
+ add_log(f"[LOG 8] ํ•ด์ƒ๋„ {resolution_factor*100:.1f}%๋กœ ์กฐ์ ˆ ์ค‘...")
96
+ clip = clip.resize(resolution_factor)
97
+
98
+ # ํ”„๋ ˆ์ž„ ๋ ˆ์ดํŠธ ์กฐ์ ˆ (๊ธฐ๋ณธ ์›๋ณธ fps)
99
+ original_fps = clip.fps
100
+ target_fps = original_fps * frame_rate_factor
101
+ add_log(f"[LOG 9] ํ”„๋ ˆ์ž„ ๋ ˆ์ดํŠธ๋ฅผ {target_fps:.2f}๋กœ ์กฐ์ ˆ ์ค‘... (์›๋ณธ {original_fps} FPS)")
102
+ clip = clip.set_fps(target_fps)
103
+
104
+ # ๋ฐ˜๋ณต ํšŒ์ˆ˜ (์ตœ๋Œ€ 10ํšŒ)
105
+ repeat_count = min(max(int(repeat_count), 1), 10)
106
+ add_log(f"[LOG 10] ๋ฐ˜๋ณต ํšŒ์ˆ˜: {repeat_count}ํšŒ")
107
+
108
+ repeated_clips = [clip] * repeat_count
109
+ final_clip = mp.concatenate_videoclips(repeated_clips)
110
+
111
+ # GIF ์ƒ์„ฑ
112
+ add_log("[LOG 11] GIF ์ƒ์„ฑ ์ค‘...")
113
+ # ์ž„์‹œ ํŒŒ์ผ ์ด๋ฆ„ ์ƒ์„ฑ
114
+ output_filename = f"temp_{uuid.uuid4().hex}.gif"
115
+
116
+ final_clip.write_gif(output_filename, fps=target_fps)
117
+
118
+ add_log("[LOG 12] GIF ์ƒ์„ฑ ์™„๋ฃŒ! ํŒŒ์ผ๋ช…: " + output_filename)
119
+
120
+ # GIF ๋ฏธ๋ฆฌ๋ณด๊ธฐ์šฉ ์ด๋ฏธ์ง€ ๋ฐ˜ํ™˜ (์ฒซ ํ”„๋ ˆ์ž„)
121
+ gif_preview = Image.open(output_filename)
122
+
123
+ return gif_preview, output_filename, "\n".join(global_logs)
124
+
125
+ def update_thumbnails(video, start_time_str, end_time_str):
126
+ """
127
+ ์‹œ์ž‘/๋ ์ธ๋„ค์ผ์„ ์—…๋ฐ์ดํŠธํ•˜๊ธฐ ์œ„ํ•œ ํ•จ์ˆ˜
128
+ """
129
+ input_video = mp.VideoFileClip(video.name)
130
+ duration = input_video.duration
131
+
132
+ start_sec = parse_time_to_seconds(start_time_str)
133
+ end_sec = parse_time_to_seconds(end_time_str)
134
+ # ๋ฒ”์œ„ ๋ณด์ •
135
+ if start_sec < 0:
136
+ start_sec = 0
137
+ if end_sec <= 0 or end_sec > duration:
138
+ end_sec = duration
139
+ if start_sec >= end_sec:
140
+ start_sec = 0
141
+ end_sec = duration
142
+
143
+ start_thumb = generate_thumbnail(input_video, start_sec)
144
+ end_thumb = generate_thumbnail(input_video, end_sec)
145
+
146
+ return start_thumb, end_thumb
147
+
148
+ # ------------------------------
149
+ # Gradio ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌ์„ฑ (Blocks)
150
+ # ------------------------------
151
+ with gr.Blocks() as demo:
152
+ gr.Markdown("## ๋™์˜์ƒ์„ GIF๋กœ ๋ณ€ํ™˜ํ•˜๊ธฐ ๋ฐ๋ชจ")
153
+
154
+ with gr.Tab("GIF ๏ฟฝ๏ฟฝ๏ฟฝํ™˜"):
155
+ # 1. ์—…๋กœ๋“œ ์ž…๋ ฅ์ฐฝ
156
+ video_input = gr.Video(label="๋™์˜์ƒ ์—…๋กœ๋“œ", type="file") # type="file"๋กœ ์—…๋กœ๋“œ ์‹œ 'video.name' ์ ‘๊ทผ ๊ฐ€๋Šฅ
157
+
158
+ # 4. ์‹œ์ž‘/๋ ๋ถ€๋ถ„ ์‹œ๊ฐ„ ์ž…๋ ฅ
159
+ start_time = gr.Textbox(label="์‹œ์ž‘ ์‹œ๊ฐ„ (์˜ˆ: 00:00:05)", value="00:00:00")
160
+ end_time = gr.Textbox(label="์ข…๋ฃŒ ์‹œ๊ฐ„ (์˜ˆ: 00:00:10)", value="00:00:05")
161
+
162
+ # ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์ธ๋„ค์ผ (์‹œ์ž‘, ์ข…๋ฃŒ)
163
+ start_thumb_output = gr.Image(label="์‹œ์ž‘ ์ธ๋„ค์ผ ๋ฏธ๋ฆฌ๋ณด๊ธฐ")
164
+ end_thumb_output = gr.Image(label="์ข…๋ฃŒ ์ธ๋„ค์ผ ๋ฏธ๋ฆฌ๋ณด๊ธฐ")
165
+
166
+ # 6. ํ•ด์ƒ๋„ ์กฐ์ ˆ ์Šฌ๋ผ์ด๋”(๊ธฐ๋ณธ 1.0)
167
+ resolution_slider = gr.Slider(label="ํ•ด์ƒ๋„ ๋น„์œจ ์กฐ์ ˆ(0.1 ~ 1.0)", minimum=0.1, maximum=1.0, step=0.1, value=1.0)
168
+
169
+ # 7. ํ”„๋ ˆ์ž„ ์†๋„ ์กฐ์ ˆ ์Šฌ๋ผ์ด๋”(๊ธฐ๋ณธ 1.0)
170
+ fps_slider = gr.Slider(label="ํ”„๋ ˆ์ž„ ๋ ˆ์ดํŠธ ๋ฐฐ์œจ ์กฐ์ ˆ(0.1 ~ 2.0)", minimum=0.1, maximum=2.0, step=0.1, value=1.0)
171
+
172
+ # 8. ์†๋„ ์กฐ์ ˆ ์Šฌ๋ผ์ด๋”(๊ธฐ๋ณธ 1๋ฐฐ)
173
+ speed_slider = gr.Slider(label="์žฌ์ƒ ์†๋„ ์กฐ์ ˆ(0.5 ~ 2.0)", minimum=0.5, maximum=2.0, step=0.1, value=1.0)
174
+
175
+ # 9. ๋ฐ˜๋ณต ํšŸ์ˆ˜
176
+ repeat_slider = gr.Slider(label="GIF ๋ฐ˜๋ณต ํšŸ์ˆ˜(1 ~ 10)", minimum=1, maximum=10, step=1, value=1)
177
+
178
+ # 10. GIF ์ƒ์„ฑ ๋ฒ„ํŠผ
179
+ generate_button = gr.Button("GIF ์ƒ์„ฑํ•˜๊ธฐ")
180
+
181
+ # 11, 12. ๊ฒฐ๊ณผ ๋ฏธ๋ฆฌ๋ณด๊ธฐ(์ด๋ฏธ์ง€), ๋‹ค์šด๋กœ๋“œ(๋งํฌ)
182
+ gif_preview_output = gr.Image(label="์™„์„ฑ๋œ GIF ๋ฏธ๋ฆฌ๋ณด๊ธฐ")
183
+ download_output = gr.File(label="GIF ๋‹ค์šด๋กœ๋“œ ๋งํฌ")
184
+
185
+ # ๋กœ๊ทธ ์ถœ๋ ฅ
186
+ logs_output = gr.Textbox(label="๋กœ๊ทธ ์ถœ๋ ฅ", lines=10)
187
+
188
+ # ์ธ๋„ค์ผ ์—…๋ฐ์ดํŠธ ์ด๋ฒคํŠธ
189
+ # (์ž…๋ ฅ ์‹œ๊ฐ„์ด ๋ณ€ํ•  ๋•Œ๋งˆ๋‹ค ์ž๋™์œผ๋กœ ์ธ๋„ค์ผ ๊ฐฑ์‹ )
190
+ start_time.change(
191
+ fn=update_thumbnails,
192
+ inputs=[video_input, start_time, end_time],
193
+ outputs=[start_thumb_output, end_thumb_output]
194
+ )
195
+ end_time.change(
196
+ fn=update_thumbnails,
197
+ inputs=[video_input, start_time, end_time],
198
+ outputs=[start_thumb_output, end_thumb_output]
199
+ )
200
+
201
+ # ์˜์ƒ ์—…๋กœ๋“œ๋  ๋•Œ๋„ ์ธ๋„ค์ผ ์ž๋™ ์ƒ์„ฑ
202
+ video_input.change(
203
+ fn=update_thumbnails,
204
+ inputs=[video_input, start_time, end_time],
205
+ outputs=[start_thumb_output, end_thumb_output]
206
+ )
207
+
208
+ # GIF ์ƒ์„ฑ ๋ฒ„ํŠผ ๋™์ž‘
209
+ generate_button.click(
210
+ fn=process_video,
211
+ inputs=[
212
+ video_input,
213
+ start_time,
214
+ end_time,
215
+ resolution_slider,
216
+ fps_slider,
217
+ speed_slider,
218
+ repeat_slider
219
+ ],
220
+ outputs=[
221
+ gif_preview_output,
222
+ download_output,
223
+ logs_output
224
+ ]
225
+ )
226
+
227
+ gr.Markdown("### [์‚ฌ์šฉ ๊ฐ€์ด๋“œ]\n1. ๋™์˜์ƒ์„ ์—…๋กœ๋“œํ•˜์„ธ์š”.\n2. ์‹œ์ž‘/๋ ์‹œ๊ฐ„์„ ์ ์ ˆํžˆ ์ž…๋ ฅํ•˜์„ธ์š”.\n3. ํ•ด์ƒ๋„, ํ”„๋ ˆ์ž„ ์†๋„, ์žฌ์ƒ ์†๋„, ๋ฐ˜๋ณต ํšŸ์ˆ˜๋ฅผ ์กฐ์ ˆํ•œ ๋’ค `GIF ์ƒ์„ฑํ•˜๊ธฐ` ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด GIF๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.\n4. ๊ฒฐ๊ณผ ๋ฏธ๋ฆฌ๋ณด๊ธฐ์™€ ๋‹ค์šด๋กœ๋“œ ๋งํฌ๋ฅผ ํ†ตํ•ด GIF๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.")
228
+
229
+ # ์‹คํ–‰ (Spaces์—์„œ๋Š” demo.launch()๋ฅผ ์ƒ๋žตํ•˜๊ฑฐ๋‚˜ ๊ทธ๋Œ€๋กœ ๋‘์–ด๋„ ๋ฉ๋‹ˆ๋‹ค)
230
+ if __name__ == "__main__":
231
+ demo.launch()
232
+