Kims12 commited on
Commit
02446f3
ยท
verified ยท
1 Parent(s): d6edaa0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +236 -99
app.py CHANGED
@@ -64,7 +64,7 @@ def adjust_aspect_ratio(clip, option):
64
  width, height = clip.size
65
  current_ratio = width / height
66
 
67
- # ์˜์ƒ์ด ๋„ˆ๋ฌด ๋„“๋‹ค๋ฉด ๊ฐ€๋กœ๋ฅผ, ๋„ˆ๋ฌด ๋†’๋‹ค๋ฉด ์„ธ๋กœ๋ฅผ ํฌ๋กญ
68
  if current_ratio > target_ratio:
69
  new_width = int(height * target_ratio)
70
  new_height = height
@@ -130,48 +130,33 @@ def process_video(video,
130
  add_log(f"[LOG 7-1] ์ถœ๋ ฅ ํ•ด์ƒ๋„ ์ถ•์†Œ: {resolution_scale*100:.0f}%")
131
  clip = clip.resize(resolution_scale)
132
 
133
- # ์žฌ์ƒ์†๋„ ์กฐ์ ˆ: speedx๋ฅผ ์ ์šฉํ•˜๋ฉด clip.duration์€ ์ค„์–ด๋“ค์ง€๋งŒ fps๋Š” ๊ทธ๋Œ€๋กœ ์œ ์ง€๋จ.
134
  if abs(speed_factor - 1.0) > 1e-3:
135
  add_log(f"[LOG 8] ์žฌ์ƒ์†๋„ {speed_factor}๋ฐฐ๋กœ ์กฐ์ ˆ ์ค‘...")
136
  clip = clip.fx(mp.vfx.speedx, speed_factor)
137
 
138
- # FPS ์กฐ์ ˆ: ์ตœ์ข… ์ถœ๋ ฅ FPS๋Š” ํ”„๋ ˆ์ž„ ๋ ˆ์ดํŠธ ๋ฐฐ์œจ๋งŒ ๋ฐ˜์˜ (speed_factor๋Š” ์ด๋ฏธ duration์— ๋ฐ˜์˜๋จ)
139
  original_fps = clip.fps
140
  target_fps = original_fps * frame_rate_factor
141
  add_log(f"[LOG 9] ์ตœ์ข… ์ถœ๋ ฅ FPS: {target_fps:.2f} (์›๋ณธ FPS: {original_fps})")
142
  clip = clip.set_fps(target_fps)
143
 
144
- # ๋ฐ˜๋ณต ํšŸ์ˆ˜: 0์ด๋ฉด ๋ฌดํ•œ๋ฐ˜๋ณต, 1~10์ด๋ฉด ํ•ด๋‹น ํšŸ์ˆ˜๋งŒํผ ๋ฐ˜๋ณต (GIF์˜ loop ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์‚ฌ์šฉ)
145
- loop_param = 0 if int(repeat_count) == 0 else int(repeat_count)
146
- add_log(f"[LOG 10] GIF ๋ฐ˜๋ณต ํšŸ์ˆ˜ ์„ค์ •: {repeat_count} (0์ด๋ฉด ๋ฌดํ•œ๋ฐ˜๋ณต, ์ ์šฉ๊ฐ’: {loop_param})")
147
-
148
  add_log("[LOG 11] GIF ์ƒ์„ฑ ์ค‘...")
149
  output_filename = f"temp_{uuid.uuid4().hex}.gif"
150
  try:
151
- # MoviePy์˜ write_gif ๋Œ€์‹  ์ง์ ‘ imageio๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ GIF ์ƒ์„ฑ
152
- import imageio
153
-
154
- # ํ”„๋ ˆ์ž„ ์ถ”์ถœ
155
- frames = []
156
- for t in range(int(clip.duration * target_fps)):
157
- frame_time = t / target_fps
158
- frame = clip.get_frame(frame_time)
159
- frames.append(frame)
160
-
161
- # imageio๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ GIF ์ €์žฅ (loop ํŒŒ๋ผ๋ฏธํ„ฐ ์ ์šฉ)
162
- imageio.mimsave(
163
- output_filename,
164
- frames,
165
- fps=target_fps,
166
- loop=loop_param # ์—ฌ๊ธฐ์„œ ๋ฐ˜๋ณต ํšŸ์ˆ˜ ์„ค์ •
167
- )
168
-
169
- add_log("[LOG 12] GIF ์ƒ์„ฑ ์™„๋ฃŒ! ํŒŒ์ผ๋ช…: " + output_filename)
170
  except Exception as e:
171
  add_log(f"[ERROR] GIF ์ƒ์„ฑ ์‹คํŒจ: {e}")
172
  return None, None, "\n".join(global_logs)
173
 
174
- # ๋ฏธ๋ฆฌ๋ณด๊ธฐ: ์™„์„ฑ๋œ GIF ํŒŒ์ผ ๊ฒฝ๋กœ๋ฅผ ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜ํ•˜๋ฉด gr.Image์—์„œ ์• ๋‹ˆ๋ฉ”์ด์…˜ ๋ฏธ๋ฆฌ๋ณด๊ธฐ๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
175
  return output_filename, output_filename, "\n".join(global_logs)
176
 
177
  def update_thumbnails(video, start_time_str, end_time_str):
@@ -207,78 +192,230 @@ def update_thumbnails(video, start_time_str, end_time_str):
207
  # Gradio ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌ์„ฑ (Blocks)
208
  # ------------------------------
209
  with gr.Blocks() as demo:
210
- gr.Markdown("## ๋™์˜์ƒ์„ GIF๋กœ ๋ณ€ํ™˜ํ•˜๊ธฐ ๋ฐ๋ชจ")
211
-
212
- with gr.Tab("GIF ๋ณ€ํ™˜"):
213
- # ๋™์˜์ƒ ์—…๋กœ๋“œ
214
- video_input = gr.Video(label="๋™์˜์ƒ ์—…๋กœ๋“œ")
215
- # ์‹œ์ž‘/๋ ์‹œ๊ฐ„ ์ž…๋ ฅ
216
- start_time = gr.Textbox(label="์‹œ์ž‘ ์‹œ๊ฐ„ (์˜ˆ: 00:00:05)", value="00:00:00")
217
- end_time = gr.Textbox(label="์ข…๋ฃŒ ์‹œ๊ฐ„ (์˜ˆ: 00:00:10)", value="00:00:05")
218
- # ์ธ๋„ค์ผ ๋ฏธ๋ฆฌ๋ณด๊ธฐ
219
- start_thumb_output = gr.Image(label="์‹œ์ž‘ ์ธ๋„ค์ผ ๋ฏธ๋ฆฌ๋ณด๊ธฐ")
220
- end_thumb_output = gr.Image(label="์ข…๋ฃŒ ์ธ๋„ค์ผ ๋ฏธ๋ฆฌ๋ณด๊ธฐ")
221
- # ํ”Œ๋žซํผ๋ณ„ ํ•ด์ƒ๋„(๋น„์œจ) ์„ ํƒ (Radio ๋ฒ„ํŠผ)
222
- platform_option = gr.Radio(
223
- label="ํ•ด์ƒ๋„/๋น„์œจ ์„ ํƒ",
224
- choices=["์›๋ณธ ์œ ์ง€", "์œ ํŠœ๋ธŒ (16:9)", "์‡ผ์ธ /๋ฆด์Šค (9:16)", "์ •์‚ฌ๊ฐํ˜• (1:1)", "์ธ์Šคํƒ€๊ทธ๋žจ (4:5)", "ํด๋ž˜์‹ (4:3)"],
225
- value="์›๋ณธ ์œ ์ง€"
226
- )
227
- # ์ถœ๋ ฅ ํ•ด์ƒ๋„ ์ถ•์†Œ ์˜ต์…˜ (1.0์ด๋ฉด ์›๋ณธ, 0.1 ~ 1.0 ๋ฒ”์œ„)
228
- resolution_scale_slider = gr.Slider(label="์ถœ๋ ฅ ํ•ด์ƒ๋„ ์ถ•์†Œ ๋น„์œจ (0.1 ~ 1.0)",
229
- minimum=0.1, maximum=1.0, step=0.1, value=1.0)
230
- # ํ”„๋ ˆ์ž„ ๋ ˆ์ดํŠธ, ์žฌ์ƒ ์†๋„, ๋ฐ˜๋ณต ํšŸ์ˆ˜ ์กฐ์ ˆ
231
- frame_rate_slider = gr.Slider(label="ํ”„๋ ˆ์ž„ ๋ ˆ์ดํŠธ ๋ฐฐ์œจ ์กฐ์ ˆ (0.1 ~ 1.0)",
232
- minimum=0.1, maximum=1.0, step=0.1, value=1.0)
233
- speed_slider = gr.Slider(label="์žฌ์ƒ ์†๋„ ์กฐ์ ˆ (0.5 ~ 5.0)",
234
- minimum=0.5, maximum=5.0, step=0.1, value=1.0)
235
- repeat_slider = gr.Slider(label="GIF ๋ฐ˜๋ณต ํšŸ์ˆ˜ (0: ๋ฌดํ•œ๋ฐ˜๋ณต, 1~10: ๋ฐ˜๋ณต ํšŸ์ˆ˜)",
236
- minimum=0, maximum=10, step=1, value=0)
237
- # GIF ์ƒ์„ฑ ๋ฒ„ํŠผ ๋ฐ ๊ฒฐ๊ณผ ์ถœ๋ ฅ
238
- generate_button = gr.Button("GIF ์ƒ์„ฑํ•˜๊ธฐ")
239
- gif_preview_output = gr.Image(label="์™„์„ฑ๋œ GIF ๋ฏธ๋ฆฌ๋ณด๊ธฐ")
240
- download_output = gr.File(label="GIF ๋‹ค์šด๋กœ๋“œ ๋งํฌ")
241
- logs_output = gr.Textbox(label="๋กœ๊ทธ ์ถœ๋ ฅ", lines=10)
242
-
243
- # ์ธ๋„ค์ผ ์—…๋ฐ์ดํŠธ ์ด๋ฒคํŠธ (๋™์˜์ƒ ๋˜๋Š” ์‹œ๊ฐ„ ์ž…๋ ฅ ๋ณ€๊ฒฝ ์‹œ)
244
- start_time.change(fn=update_thumbnails,
245
- inputs=[video_input, start_time, end_time],
246
- outputs=[start_thumb_output, end_thumb_output])
247
- end_time.change(fn=update_thumbnails,
248
- inputs=[video_input, start_time, end_time],
249
- outputs=[start_thumb_output, end_thumb_output])
250
- video_input.change(fn=update_thumbnails,
251
- inputs=[video_input, start_time, end_time],
252
- outputs=[start_thumb_output, end_thumb_output])
253
- # GIF ์ƒ์„ฑ ๋ฒ„ํŠผ ์ด๋ฒคํŠธ
254
- generate_button.click(
255
- fn=process_video,
256
- inputs=[
257
- video_input, # ๋™์˜์ƒ ์—…๋กœ๋“œ
258
- start_time, # ์‹œ์ž‘ ์‹œ๊ฐ„
259
- end_time, # ์ข…๋ฃŒ ์‹œ๊ฐ„
260
- platform_option, # ํ”Œ๋žซํผ๋ณ„ ๋น„์œจ
261
- frame_rate_slider, # ํ”„๋ ˆ์ž„ ๋ ˆ์ดํŠธ ๋ฐฐ์œจ
262
- speed_slider, # ์žฌ์ƒ ์†๋„
263
- repeat_slider, # ๋ฐ˜๋ณต ํšŸ์ˆ˜
264
- resolution_scale_slider # ํ•ด์ƒ๋„ ์ถ•์†Œ ๋น„์œจ
265
- ],
266
- outputs=[
267
- gif_preview_output, # ์™„์„ฑ๋œ GIF ๋ฏธ๋ฆฌ๋ณด๊ธฐ
268
- download_output, # GIF ๋‹ค์šด๋กœ๋“œ ๋งํฌ
269
- logs_output # ๋กœ๊ทธ ์ถœ๋ ฅ
270
- ]
271
- )
272
-
273
- gr.Markdown(
274
- "### [์‚ฌ์šฉ ๊ฐ€์ด๋“œ]\n"
275
- "1. ๋™์˜์ƒ์„ ์—…๋กœ๋“œํ•˜์„ธ์š”.\n"
276
- "2. ์‹œ์ž‘/๋ ์‹œ๊ฐ„์„ ์ž…๋ ฅํ•˜์„ธ์š”.\n"
277
- "3. ํ”Œ๋žซํผ๋ณ„ ๊ถŒ์žฅ ํ•ด์ƒ๋„/๋น„์œจ(์›๋ณธ ์œ ์ง€, ์œ ํŠœ๋ธŒ, ์‡ผ์ธ /๋ฆด์Šค, ์ •์‚ฌ๊ฐํ˜•, ์ธ์Šคํƒ€๊ทธ๋žจ, ํด๋ž˜์‹)๊ณผ ์ถœ๋ ฅ ํ•ด์ƒ๋„ ์ถ•์†Œ ๋น„์œจ์„ ์„ ํƒํ•˜๊ณ ,\n"
278
- " ํ”„๋ ˆ์ž„ ๋ ˆ์ดํŠธ ๋ฐฐ์œจ, ์žฌ์ƒ ์†๋„, ๋ฐ˜๋ณต ํšŸ์ˆ˜๋ฅผ ์กฐ์ ˆํ•œ ํ›„ `GIF ์ƒ์„ฑํ•˜๊ธฐ` ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด GIF๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.\n"
279
- " - ๋ฐ˜๋ณต ํšŸ์ˆ˜(0: ๋ฌดํ•œ๋ฐ˜๋ณต, 1~10: ์ง€์ • ํšŸ์ˆ˜). ์˜ˆ) 1์ด๋ฉด 1ํšŒ ์žฌ์ƒ ํ›„ ๋ฉˆ์ถค, 2์ด๋ฉด 2ํšŒ ์žฌ์ƒ, 0์ด๋ฉด ๋ฌดํ•œ๋ฐ˜๋ณต.\n"
280
- "4. ๊ฒฐ๊ณผ ๋ฏธ๋ฆฌ๋ณด๊ธฐ์™€ ๋‹ค์šด๋กœ๋“œ ๋งํฌ๋ฅผ ํ†ตํ•ด GIF๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.\n"
281
- "โ€ป ๋ฌดํ•œ๋ฐ˜๋ณต์ด ์•„๋‹Œ ํŠน์ • ๋ฐ˜๋ณต ํšŸ์ˆ˜๋ฅผ ์›ํ•  ๊ฒฝ์šฐ, ImageMagick์ด ์„ค์น˜๋˜์–ด ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
282
  )
283
 
284
  if __name__ == "__main__":
 
64
  width, height = clip.size
65
  current_ratio = width / height
66
 
67
+ # ์˜์ƒ์ด ๋„ˆ๋ฌด ๋„“๋‹ค๋ฉด ๊ฐ€๋กœ๋ฅผ, ๋„ˆ๋ฌด ๋†’๋‹ค๋ฉด ์„ธ๋กœ๋ฅผ ํฌ๋กญํ•ฉ๋‹ˆ๋‹ค.
68
  if current_ratio > target_ratio:
69
  new_width = int(height * target_ratio)
70
  new_height = height
 
130
  add_log(f"[LOG 7-1] ์ถœ๋ ฅ ํ•ด์ƒ๋„ ์ถ•์†Œ: {resolution_scale*100:.0f}%")
131
  clip = clip.resize(resolution_scale)
132
 
133
+ # ์žฌ์ƒ์†๋„ ์กฐ์ ˆ
134
  if abs(speed_factor - 1.0) > 1e-3:
135
  add_log(f"[LOG 8] ์žฌ์ƒ์†๋„ {speed_factor}๋ฐฐ๋กœ ์กฐ์ ˆ ์ค‘...")
136
  clip = clip.fx(mp.vfx.speedx, speed_factor)
137
 
138
+ # FPS ์กฐ์ ˆ
139
  original_fps = clip.fps
140
  target_fps = original_fps * frame_rate_factor
141
  add_log(f"[LOG 9] ์ตœ์ข… ์ถœ๋ ฅ FPS: {target_fps:.2f} (์›๋ณธ FPS: {original_fps})")
142
  clip = clip.set_fps(target_fps)
143
 
144
+ # ๋ฐ˜๋ณต ํšŸ์ˆ˜(0: ๋ฌดํ•œ, 1~10: ์ง€์ • ํšŸ์ˆ˜)
145
+ add_log(f"[LOG 10] GIF ๋ฐ˜๋ณต ํšŸ์ˆ˜ ์„ค์ •: {repeat_count} (0์ด๋ฉด ๋ฌดํ•œ๋ฐ˜๋ณต)")
146
+ final_clip = clip
147
+
148
  add_log("[LOG 11] GIF ์ƒ์„ฑ ์ค‘...")
149
  output_filename = f"temp_{uuid.uuid4().hex}.gif"
150
  try:
151
+ loop_param = 0 if int(repeat_count) == 0 else int(repeat_count)
152
+ # ImageMagick๋กœ GIF ์ƒ์„ฑ (loop ์ ์šฉ)
153
+ final_clip.write_gif(output_filename, fps=target_fps, loop=loop_param)
154
+ add_log(f"[LOG 12] GIF ์ƒ์„ฑ ์™„๋ฃŒ! ํŒŒ์ผ๋ช…: {output_filename}, loop={loop_param}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
155
  except Exception as e:
156
  add_log(f"[ERROR] GIF ์ƒ์„ฑ ์‹คํŒจ: {e}")
157
  return None, None, "\n".join(global_logs)
158
 
159
+ # ๊ฒฐ๊ณผ ๋ฐ˜ํ™˜ (๋ฏธ๋ฆฌ๋ณด๊ธฐ์šฉ ๊ฒฝ๋กœ, ๋‹ค์šด๋กœ๋“œ์šฉ ๊ฒฝ๋กœ, ๋กœ๊ทธ)
160
  return output_filename, output_filename, "\n".join(global_logs)
161
 
162
  def update_thumbnails(video, start_time_str, end_time_str):
 
192
  # Gradio ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌ์„ฑ (Blocks)
193
  # ------------------------------
194
  with gr.Blocks() as demo:
195
+ #
196
+ # 1) CSS ์Šคํƒ€์ผ: ์ตœ๋Œ€ํ•œ Gradio ๋А๋‚Œ์„ ์ˆจ๊ธฐ๊ณ , ์ปค์Šคํ…€ ๋””์ž์ธ ์ ์šฉ
197
+ #
198
+ custom_css = """
199
+ <style>
200
+ /* ๊ธฐ๋ณธ์ ์ธ ๋ฆฌ์…‹ */
201
+ body, h1, h2, h3, h4, p, div, span {
202
+ margin: 0; padding: 0; box-sizing: border-box;
203
+ font-family: "Noto Sans", sans-serif;
204
+ }
205
+
206
+ /* ๋ฐฐ๊ฒฝ์ƒ‰, ๋งˆ์ง„ ๋“ฑ ๊ธฐ๋ณธ ์„ค์ • */
207
+ body {
208
+ background: #fefefe;
209
+ color: #333;
210
+ padding: 20px;
211
+ }
212
+
213
+ /* ํฐ ํƒ€์ดํ‹€ */
214
+ .main-title {
215
+ font-size: 2.0rem;
216
+ text-align: center;
217
+ margin: 0.5em 0;
218
+ color: #e67e22; /* ์˜ค๋ Œ์ง€ ํ†ค */
219
+ font-weight: bold;
220
+ }
221
+
222
+ /* ์•ˆ๋‚ด ๋ฌธ๊ตฌ (์‚ฌ์šฉ ๊ฐ€์ด๋“œ ๋“ฑ) */
223
+ .guide-text {
224
+ font-size: 1rem;
225
+ color: #555;
226
+ margin-bottom: 1.5em;
227
+ text-align: center;
228
+ line-height: 1.4em;
229
+ }
230
+
231
+ .emoji-title {
232
+ font-size: 1.3rem;
233
+ margin-bottom: 0.5em;
234
+ text-align: center;
235
+ color: #2ecc71; /* ๊ทธ๋ฆฐ ํ†ค */
236
+ }
237
+
238
+ /* ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์ขŒ์šฐ๋กœ ๋ฐฐ์น˜ */
239
+ .row-container {
240
+ display: flex;
241
+ flex-direction: row;
242
+ justify-content: space-between;
243
+ margin-bottom: 1.5em;
244
+ gap: 1em;
245
+ }
246
+
247
+ /* 2๊ฐœ์˜ ๋ฐ•์Šค๋ฅผ ๊ฐ๊ฐ ๋ฐ˜์œผ๋กœ ๋‚˜๋ˆ„์–ด ๋ฐฐ์น˜ */
248
+ .box {
249
+ width: 50%;
250
+ background-color: #ffffff;
251
+ border: 2px solid #ddd;
252
+ border-radius: 15px;
253
+ padding: 1em;
254
+ box-shadow: 0 2px 5px rgba(0,0,0,0.1);
255
+ }
256
+
257
+ /* 1๊ฐœ์˜ ๋ฐ•์Šค ์ „์ฒด ๋„ˆ๋น„ */
258
+ .single-box {
259
+ width: 100%;
260
+ background-color: #ffffff;
261
+ border: 2px solid #ddd;
262
+ border-radius: 15px;
263
+ padding: 1em;
264
+ box-shadow: 0 2px 5px rgba(0,0,0,0.1);
265
+ margin-bottom: 1.5em;
266
+ }
267
+
268
+ /* ์„น์…˜ ๋‚ด๋ถ€ ํ—ค๋” ์Šคํƒ€์ผ */
269
+ .section-title {
270
+ font-size: 1.2rem;
271
+ margin-bottom: 0.5em;
272
+ font-weight: bold;
273
+ color: #3498db; /* ๋ธ”๋ฃจ ํ†ค */
274
+ }
275
+
276
+ /* ์ปดํฌ๋„ŒํŠธ ๋ฐฐ์น˜ ์œ„ํ•œ ๋‚ด๋ถ€ ๋ž˜ํผ */
277
+ .component-wrapper {
278
+ margin-bottom: 0.8em;
279
+ }
280
+
281
+ /* "GIF ์ƒ์„ฑํ•˜๊ธฐ" ๋ฒ„ํŠผ ์Šคํƒ€์ผ(์•ฝ๊ฐ„ ํฌ๊ฒŒ) */
282
+ .big-button {
283
+ display: block;
284
+ width: 100%;
285
+ padding: 0.8em;
286
+ background-color: #e74c3c;
287
+ color: #fff;
288
+ border: none;
289
+ border-radius: 8px;
290
+ font-size: 1.1rem;
291
+ font-weight: bold;
292
+ cursor: pointer;
293
+ margin-top: 1em;
294
+ }
295
+ .big-button:hover {
296
+ background-color: #c0392b;
297
+ }
298
+ </style>
299
+ """
300
+
301
+ gr.HTML(custom_css) # ์ปค์Šคํ…€ CSS๋ฅผ ์‚ฝ์ž…
302
+
303
+ # 2) ์ œ๋ชฉ ๋ฐ ๊ฐ„๋‹จ ์„ค๋ช…
304
+ gr.HTML(
305
+ """
306
+ <h1 class="main-title">๐ŸŽฌ ์˜์ƒ์„ GIF๋กœ ๋ณ€ํ™˜ํ•˜๊ธฐ ๐Ÿฟ</h1>
307
+ <div class="guide-text">
308
+ ๐Ÿ“ ๊ฐ„๋‹จ ์•ˆ๋‚ด: ์˜์ƒ์„ ์—…๋กœ๋“œํ•˜๊ณ , ์›ํ•˜๋Š” ํ•ด์ƒ๋„/๋น„์œจ, ํ”„๋ ˆ์ž„์†๋„, ๋ฐ˜๋ณต ํšŸ์ˆ˜๋ฅผ ์„ค์ •ํ•œ ๋’ค GIF ์ƒ์„ฑํ•˜๊ธฐ๋ฅผ ๋ˆŒ๋Ÿฌ๋ณด์„ธ์š”!<br/>
309
+ </div>
310
+ """
311
+ )
312
+
313
+ # 3) ์ฒซ ๋ฒˆ์งธ ์ค„: ์ž…๋ ฅ๋ถ€(์ขŒ์ธก) + ์ถœ๋ ฅ๋ถ€(์šฐ์ธก)
314
+ gr.HTML("<div class='row-container'>")
315
+ gr.HTML("<div class='box'>") # ์ขŒ์ธก ์ž…๋ ฅ๋ถ€ ๋ฐ•์Šค ์‹œ์ž‘
316
+ gr.HTML("<div class='emoji-title'>โš™๏ธ ์ž…๋ ฅ๋ถ€</div>")
317
+ # ์˜์ƒ ์—…๋กœ๋“œ
318
+ video_input = gr.Video(label="์˜์ƒ ์—…๋กœ๋“œ", elem_id="video_input", show_label=True)
319
+ platform_option = gr.Radio(
320
+ label="ํ•ด์ƒ๋„/๋น„์œจ ์„ ํƒ",
321
+ choices=["์›๋ณธ ์œ ์ง€", "์œ ํŠœ๋ธŒ (16:9)", "์‡ผ์ธ /๋ฆด์Šค (9:16)", "์ •์‚ฌ๊ฐํ˜• (1:1)", "์ธ์Šคํƒ€๊ทธ๋žจ (4:5)", "ํด๋ž˜์‹ (4:3)"],
322
+ value="์›๋ณธ ์œ ์ง€",
323
+ elem_id="platform_option"
324
+ )
325
+ resolution_scale_slider = gr.Slider(
326
+ label="์ถœ๋ ฅ ํ•ด์ƒ๋„ ์ถ•์†Œ ๋น„์œจ (0.1 ~ 1.0)",
327
+ minimum=0.1, maximum=1.0, step=0.1, value=1.0,
328
+ elem_id="resolution_scale"
329
+ )
330
+ frame_rate_slider = gr.Slider(
331
+ label="ํ”„๋ ˆ์ž„ ๋ ˆ์ดํŠธ ๋ฐฐ์œจ ์กฐ์ ˆ (0.1 ~ 1.0)",
332
+ minimum=0.1, maximum=1.0, step=0.1, value=1.0,
333
+ elem_id="frame_rate"
334
+ )
335
+ speed_slider = gr.Slider(
336
+ label="์žฌ์ƒ ์†๋„ ์กฐ์ ˆ (0.5 ~ 5.0)",
337
+ minimum=0.5, maximum=5.0, step=0.1, value=1.0,
338
+ elem_id="speed_factor"
339
+ )
340
+ repeat_slider = gr.Slider(
341
+ label="GIF ๋ฐ˜๋ณต ํšŸ์ˆ˜ (0: ๋ฌดํ•œ๋ฐ˜๋ณต, 1~10: ๋ฐ˜๋ณต ํšŸ์ˆ˜)",
342
+ minimum=0, maximum=10, step=1, value=0,
343
+ elem_id="repeat_count"
344
+ )
345
+ gr.HTML("</div>") # ์ขŒ์ธก ์ž…๋ ฅ๋ถ€ ๋ฐ•์Šค ๋
346
+
347
+ gr.HTML("<div class='box'>") # ์šฐ์ธก ์ถœ๋ ฅ๋ถ€ ๋ฐ•์Šค ์‹œ์ž‘
348
+ gr.HTML("<div class='emoji-title'>๐Ÿš€ ์ถœ๋ ฅ๋ถ€</div>")
349
+ gif_preview_output = gr.Image(label="GIF ๊ฒฐ๊ณผ ๋ฏธ๋ฆฌ๋ณด๊ธฐ", elem_id="gif_preview")
350
+ download_output = gr.File(label="GIF ๋‹ค์šด๋กœ๋“œ", elem_id="gif_download")
351
+ gr.HTML("</div>") # ์šฐ์ธก ์ถœ๋ ฅ๋ถ€ ๋ฐ•์Šค ๋
352
+ gr.HTML("</div>") # ์ฒซ ๋ฒˆ์งธ ์ค„(row-container) ์ข…๋ฃŒ
353
+
354
+ # 4) ๋‘ ๋ฒˆ์งธ ์ค„: ์ธ๋„ค์ผ ๋ฏธ๋ฆฌ๋ณด๊ธฐ
355
+ gr.HTML("<div class='single-box'>")
356
+ gr.HTML("<div class='emoji-title'>๐Ÿ”Ž ์ž‘์—… ์ธ๋„ค์ผ ๋ฏธ๋ฆฌ๋ณด๊ธฐ</div>")
357
+ start_thumb_output = gr.Image(label="์‹œ์ž‘ ์ธ๋„ค์ผ", elem_id="start_thumb")
358
+ end_thumb_output = gr.Image(label="์ข…๋ฃŒ ์ธ๋„ค์ผ", elem_id="end_thumb")
359
+ # GIF ์ƒ์„ฑ ๋ฒ„ํŠผ
360
+ generate_button = gr.Button("๐Ÿ’ก GIF ์ƒ์„ฑํ•˜๊ธฐ", elem_id="generate_btn", css="", variant="primary").style(full_width=False)
361
+ logs_output = gr.Textbox(label="์ž‘์—… ๋กœ๊ทธ", lines=6)
362
+ gr.HTML("</div>")
363
+
364
+ # 5) ์ด๋ฒคํŠธ & ๋กœ์ง ์—ฐ๊ฒฐ
365
+ start_time = gr.Textbox(label="์‹œ์ž‘ ์‹œ๊ฐ„ (์˜ˆ: 00:00:05)", value="00:00:00")
366
+ end_time = gr.Textbox(label="์ข…๋ฃŒ ์‹œ๊ฐ„ (์˜ˆ: 00:00:10)", value="00:00:05")
367
+
368
+ # ํ™”๋ฉด์ƒ์—๋Š” ํ‘œ์‹œ๋˜์ง€ ์•Š์ง€๋งŒ, ๋กœ์ง ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•ด ์œ„์น˜๋งŒ ์žก์•„๋‘  (elem_id ์—†์ด)
369
+ # -> ํ•„์š”ํ•˜๋‹ค๋ฉด style="display:none;"๋กœ ์ˆจ๊ฒจ๋„ ๋จ.
370
+ start_time.change(
371
+ fn=update_thumbnails,
372
+ inputs=[video_input, start_time, end_time],
373
+ outputs=[start_thumb_output, end_thumb_output]
374
+ )
375
+ end_time.change(
376
+ fn=update_thumbnails,
377
+ inputs=[video_input, start_time, end_time],
378
+ outputs=[start_thumb_output, end_thumb_output]
379
+ )
380
+ video_input.change(
381
+ fn=update_thumbnails,
382
+ inputs=[video_input, start_time, end_time],
383
+ outputs=[start_thumb_output, end_thumb_output]
384
+ )
385
+
386
+ generate_button.click(
387
+ fn=process_video,
388
+ inputs=[
389
+ video_input, # ๋™์˜์ƒ ์—…๋กœ๋“œ
390
+ start_time, # ์‹œ์ž‘ ์‹œ๊ฐ„
391
+ end_time, # ์ข…๋ฃŒ ์‹œ๊ฐ„
392
+ platform_option, # ํ”Œ๋žซํผ๋ณ„ ๋น„์œจ
393
+ frame_rate_slider, # ํ”„๋ ˆ์ž„ ๋ ˆ์ดํŠธ ๋ฐฐ์œจ
394
+ speed_slider, # ์žฌ์ƒ ์†๋„
395
+ repeat_slider, # ๋ฐ˜๋ณต ํšŸ์ˆ˜
396
+ resolution_scale_slider # ํ•ด์ƒ๋„ ์ถ•์†Œ ๋น„์œจ
397
+ ],
398
+ outputs=[
399
+ gif_preview_output, # ์™„์„ฑ๋œ GIF ๋ฏธ๋ฆฌ๋ณด๊ธฐ
400
+ download_output, # GIF ๋‹ค์šด๋กœ๋“œ ๋งํฌ
401
+ logs_output # ๋กœ๊ทธ ์ถœ๋ ฅ
402
+ ]
403
+ )
404
+
405
+ # 6) ์‚ฌ์šฉ์ž ๊ฐ€์ด๋“œ(ํ•˜๋‹จ)
406
+ gr.HTML(
407
+ """
408
+ <div class="single-box" style="text-align:center;">
409
+ <h2 class="section-title">๐Ÿ“ข ๊ฐ„๋‹จ ์‚ฌ์šฉ ๊ฐ€์ด๋“œ</h2>
410
+ <p style="font-size:1rem; margin-bottom:0.5em;">
411
+ 1) <strong>์˜์ƒ์„ ์—…๏ฟฝ๏ฟฝ๏ฟฝ๋“œ</strong>ํ•ฉ๋‹ˆ๋‹ค.<br/>
412
+ 2) <strong>์‹œ์ž‘/๋ ์‹œ๊ฐ„</strong>๊ณผ <strong>ํ•ด์ƒ๋„/๋น„์œจ</strong>์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.<br/>
413
+ 3) <strong>์ถœ๋ ฅ ํ•ด์ƒ๋„ ์ถ•์†Œ, ํ”„๋ ˆ์ž„ ๋ ˆ์ดํŠธ, ์žฌ์ƒ ์†๋„, ๋ฐ˜๋ณต ํšŸ์ˆ˜</strong>๋ฅผ ์กฐ์ ˆํ•ฉ๋‹ˆ๋‹ค.<br/>
414
+ 4) <strong>GIF ์ƒ์„ฑํ•˜๊ธฐ</strong> ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ ๋ณ€ํ™˜์„ ์ง„ํ–‰ํ•˜์„ธ์š”.<br/>
415
+ 5) ๊ฒฐ๊ณผ ๋ฏธ๋ฆฌ๋ณด๊ธฐ์™€ ๋‹ค์šด๋กœ๋“œ๋ฅผ ํ†ตํ•ด GIF๋ฅผ ํ™•์ธํ•˜์„ธ์š”!<br/>
416
+ </p>
417
+ </div>
418
+ """
419
  )
420
 
421
  if __name__ == "__main__":