taeyeol commited on
Commit
3f777d6
Β·
verified Β·
1 Parent(s): 19e8c85

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +70 -135
app.py CHANGED
@@ -1,41 +1,29 @@
1
  import os
2
  import tempfile
3
-
4
- # κΌ­ "moviepy.editor"λ‘œλΆ€ν„° μž„ν¬νŠΈν•  ν•„μš”λŠ” μ—†κ³ , μ•„λž˜μ²˜λŸΌ 전체 μž„ν¬νŠΈ ν›„ ν•„μš”ν•œ 객체λ₯Ό 참쑰해도 λ©λ‹ˆλ‹€.
5
- # μ—¬κΈ°μ„œλŠ” 문제될 것이 μ—†μ§€λ§Œ, ν˜Ήμ‹œ ν™˜κ²½μ— 따라 경둜 importκ°€ 깨진닀면 μ•„λž˜ 방식을 μ‹œλ„ν•΄λ³Ό μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€.
6
- import moviepy.editor as me
7
-
8
- # Gradio
9
  import gradio as gr
10
 
11
- # Pillow, imageio
12
- from PIL import Image
13
  import imageio
 
 
 
14
 
15
- # ------------------------------------------------
16
- # μ „μ—­ 둜그 리슀트
17
- # ------------------------------------------------
18
  LOGS = []
19
 
20
  def log_step(message: str):
21
  LOGS.append(message)
22
- print(message) # μ½˜μ†” 둜그
23
  return "\n".join(LOGS)
24
 
25
- # ------------------------------------------------
26
- # μ‹œκ°„ λ¬Έμžμ—΄ -> 초 λ‹¨μœ„(float)
27
- # ------------------------------------------------
28
  def parse_time_str(time_str: str):
29
  try:
30
  parts = time_str.strip().split(":")
31
  if len(parts) == 3:
32
  h, m, s = parts
33
- h, m, s = int(h), int(m), float(s)
34
- return h * 3600 + m * 60 + s
35
  elif len(parts) == 2:
36
  m, s = parts
37
- m, s = int(m), float(s)
38
- return m * 60 + s
39
  elif len(parts) == 1:
40
  return float(parts[0])
41
  else:
@@ -43,174 +31,132 @@ def parse_time_str(time_str: str):
43
  except:
44
  return 0.0
45
 
46
- # ------------------------------------------------
47
- # 1) λ™μ˜μƒ μ—…λ‘œλ“œ 처리
48
- # ------------------------------------------------
49
  def upload_video(video_file):
50
  if video_file is None:
51
  return None, "μ—…λ‘œλ“œλœ 파일이 μ—†μŠ΅λ‹ˆλ‹€.", None, log_step("[단계 1] μ—…λ‘œλ“œλœ 파일이 μ—†μŠ΅λ‹ˆλ‹€.")
52
-
53
  temp_dir = tempfile.mkdtemp()
54
  temp_video_path = os.path.join(temp_dir, video_file.name)
55
  with open(temp_video_path, "wb") as f:
56
  f.write(video_file.read())
57
-
58
  try:
59
  clip = me.VideoFileClip(temp_video_path)
60
  duration_sec = clip.duration
61
  msg = f"μž¬μƒ μ‹œκ°„: {duration_sec:.2f}초"
62
- log_output = log_step("[단계 1] λΉ„λ””μ˜€ μ—…λ‘œλ“œ 및 μž¬μƒ κ°€λŠ₯ μƒνƒœ 확인 μ™„λ£Œ")
63
-
64
- # λ°˜ν™˜: (λΉ„λ””μ˜€ 경둜, μž¬μƒμ‹œκ°„ λ©”μ‹œμ§€, duration, 둜그)
65
- return temp_video_path, msg, duration_sec, log_output
66
  except Exception as e:
67
- log_output = log_step(f"[단계 1] μ—…λ‘œλ“œ μ—λŸ¬: {e}")
68
- return None, f"μ˜μƒ 처리 μ—λŸ¬: {e}", None, log_output
69
 
70
- # ------------------------------------------------
71
- # 5) 썸넀일 생성 (μ‹œμž‘/끝 μ‹œκ°„)
72
- # ------------------------------------------------
73
  def generate_thumbnails(video_path, start_time_str, end_time_str, log_history):
74
  log_step("[단계 4] μ‹œμž‘/끝 μ‹œκ°„ μž…λ ₯ μ™„λ£Œ")
75
  start_s = parse_time_str(start_time_str)
76
  end_s = parse_time_str(end_time_str)
77
-
78
  if not video_path:
79
- return None, None, log_step("[단계 5] λΉ„λ””μ˜€ λ―Έμ—…λ‘œλ“œ β†’ 썸넀일 생성 λΆˆκ°€")
80
 
81
  try:
82
  clip = me.VideoFileClip(video_path)
 
83
  if start_s < 0: start_s = 0
84
- if end_s > clip.duration: end_s = clip.duration
85
  if start_s > end_s: start_s, end_s = end_s, start_s
86
-
87
- thumb1_arr = clip.get_frame(start_s)
88
- thumb2_arr = clip.get_frame(end_s)
89
-
90
- thumb1_img = Image.fromarray(thumb1_arr)
91
- thumb2_img = Image.fromarray(thumb2_arr)
92
-
93
- log_output = log_step("[단계 5] 썸넀일 생성 μ™„λ£Œ")
94
- return thumb1_img, thumb2_img, log_output
95
  except Exception as e:
96
- log_output = log_step(f"[단계 5] 썸넀일 생성 μ—λŸ¬: {e}")
97
- return None, None, log_output
98
 
99
- # ------------------------------------------------
100
- # 10~12) GIF 생성
101
- # ------------------------------------------------
102
  def create_gif(video_path, start_time_str, end_time_str, resolution, fps, speed, loop_count, log_history):
103
  if not video_path:
104
  return None, None, log_step("[단계 10] GIF 생성 μ‹€νŒ¨: λΉ„λ””μ˜€κ°€ μ—…λ‘œλ“œλ˜μ§€ μ•ŠμŒ.")
105
 
106
  start_s = parse_time_str(start_time_str)
107
  end_s = parse_time_str(end_time_str)
108
-
109
  try:
110
  clip = me.VideoFileClip(video_path)
 
111
  if start_s < 0: start_s = 0
112
- if end_s > clip.duration: end_s = clip.duration
113
  if start_s > end_s: start_s, end_s = end_s, start_s
114
-
115
- # 1) μ„œλΈŒν΄λ¦½
116
  subclip = clip.subclip(start_s, end_s)
117
-
118
- # 2) 속도 쑰절
119
  if speed != 1.0:
120
  subclip = subclip.fx(me.vfx.speedx, speed)
121
-
122
- # 3) 해상도 쑰절
 
123
  if resolution < 100:
124
- w, h = subclip.size
125
  scale = resolution / 100.0
126
- subclip = subclip.resize((int(w*scale), int(h*scale)))
127
-
128
- # 4) FPS μ„€μ •
129
  fps = int(fps) if fps > 0 else None
130
-
131
- # 5) 반볡 횟수(0=λ¬΄ν•œ)
132
- loop = loop_count
133
-
134
  temp_dir = tempfile.mkdtemp()
135
  gif_path = os.path.join(temp_dir, "output.gif")
136
-
137
  subclip.write_gif(gif_path, fps=fps, loop=loop)
138
-
139
- log1 = log_step("[단계 10] 'GIF 생성' λ²„νŠΌ 클릭됨")
140
- log2 = log_step("[단계 11] GIF 미리보기 생성 μ™„λ£Œ")
141
- log3 = log_step("[단계 12] GIF λ‹€μš΄λ‘œλ“œ μ€€λΉ„ μ™„λ£Œ")
142
-
143
  preview_img = Image.open(gif_path)
144
-
145
- return preview_img, gif_path, "\n".join([log1, log2, log3])
146
  except Exception as e:
147
- log_output = log_step(f"[단계 10~12] GIF 생성 μ—λŸ¬: {e}")
148
- return None, None, log_output
149
 
150
- # ------------------------------------------------
151
- # Gradio μΈν„°νŽ˜μ΄μŠ€
152
- # ------------------------------------------------
153
  def build_interface():
154
  with gr.Blocks() as demo:
155
  gr.Markdown("## λ™μ˜μƒμ„ GIF둜 λ³€ν™˜ν•˜κΈ°")
156
-
157
- # (1) λΉ„λ””μ˜€ μ—…λ‘œλ“œ
158
  video_upload = gr.File(label="λΉ„λ””μ˜€ μ—…λ‘œλ“œ", file_types=["video"])
159
-
160
- # (2) μ—…λ‘œλ“œλœ μ˜μƒ 미리보기
161
  video_player = gr.Video(label="μ—…λ‘œλ“œλœ μ˜μƒ 미리보기")
162
-
163
- # (3) μ˜μƒ 정보
164
- video_info = gr.Textbox(label="μ—…λ‘œλ“œλœ μ˜μƒ 정보", interactive=False)
165
-
166
- # λ‚΄λΆ€ μƒνƒœ
167
  video_path_state = gr.State()
168
  video_duration_state = gr.State()
169
-
170
- # 둜그
171
  log_box = gr.Textbox(label="둜그", interactive=False, lines=10)
172
-
173
- # (4) μ‹œμž‘/끝 μ‹œκ°„
174
  with gr.Row():
175
- start_time = gr.Textbox(label="μ‹œμž‘ μ‹œκ°„ (HH:MM:SS / MM:SS)", value="0:00")
176
- end_time = gr.Textbox(label="끝 μ‹œκ°„ (HH:MM:SS / MM:SS)", value="0:10")
177
-
178
- # (5) 썸넀일
179
  with gr.Row():
180
  thumb1 = gr.Image(label="μ‹œμž‘ 지점 썸넀일")
181
  thumb2 = gr.Image(label="끝 지점 썸넀일")
182
-
183
- # (6) 해상도(1~100%)
184
- resolution_slider = gr.Slider(1, 100, value=100, step=1, label="해상도(%)")
185
-
186
- # (7) FPS (1~60)
187
- fps_slider = gr.Slider(1, 60, value=30, step=1, label="FPS")
188
-
189
- # (8) μ˜μƒ μž¬μƒ 속도(0.1~3.0)
190
- speed_slider = gr.Slider(0.1, 3.0, value=1.0, step=0.1, label="μž¬μƒ 속도(λ°°)")
191
-
192
- # (9) GIF 반볡 횟수 (0=λ¬΄ν•œ, ~10)
193
- loop_slider = gr.Slider(0, 10, value=0, step=1, label="GIF 반볡 횟수(0=λ¬΄ν•œ)")
194
-
195
- # (10) GIF 생성 λ²„νŠΌ
196
- generate_gif_btn = gr.Button("GIF 생성")
197
-
198
- # (11,12) κ²°κ³Ό
199
- gif_preview = gr.Image(label="GIF 미리보기")
200
  gif_download = gr.File(label="GIF λ‹€μš΄λ‘œλ“œ(ν΄λ¦­ν•˜μ—¬ μ €μž₯)")
201
-
202
- # -----------------------------
203
- # 이벀트 μ—°κ²°
204
- # -----------------------------
205
-
206
- # μ—…λ‘œλ“œ -> (λΉ„λ””μ˜€κ²½λ‘œ, 정보, 길이, 둜그)
207
  video_upload.change(
208
  fn=upload_video,
209
  inputs=video_upload,
210
  outputs=[video_path_state, video_info, video_duration_state, log_box]
211
  )
212
-
213
- # μ‹œμž‘/끝 μ‹œκ°„ -> 썸넀일
214
  start_time.change(
215
  fn=generate_thumbnails,
216
  inputs=[video_path_state, start_time, end_time, log_box],
@@ -221,23 +167,12 @@ def build_interface():
221
  inputs=[video_path_state, start_time, end_time, log_box],
222
  outputs=[thumb1, thumb2, log_box]
223
  )
224
-
225
- # GIF 생성 λ²„νŠΌ 클릭
226
  generate_gif_btn.click(
227
  fn=create_gif,
228
- inputs=[
229
- video_path_state,
230
- start_time,
231
- end_time,
232
- resolution_slider,
233
- fps_slider,
234
- speed_slider,
235
- loop_slider,
236
- log_box
237
- ],
238
  outputs=[gif_preview, gif_download, log_box]
239
  )
240
-
241
  return demo
242
 
243
  if __name__ == "__main__":
 
1
  import os
2
  import tempfile
 
 
 
 
 
 
3
  import gradio as gr
4
 
5
+ import numpy as np # (ν•„μš”ν•˜λ‹€λ©΄)
 
6
  import imageio
7
+ from PIL import Image
8
+
9
+ import moviepy.editor as me # MoviePy 전체 μž„ν¬νŠΈ
10
 
 
 
 
11
  LOGS = []
12
 
13
  def log_step(message: str):
14
  LOGS.append(message)
15
+ print(message)
16
  return "\n".join(LOGS)
17
 
 
 
 
18
  def parse_time_str(time_str: str):
19
  try:
20
  parts = time_str.strip().split(":")
21
  if len(parts) == 3:
22
  h, m, s = parts
23
+ return int(h)*3600 + int(m)*60 + float(s)
 
24
  elif len(parts) == 2:
25
  m, s = parts
26
+ return int(m)*60 + float(s)
 
27
  elif len(parts) == 1:
28
  return float(parts[0])
29
  else:
 
31
  except:
32
  return 0.0
33
 
 
 
 
34
  def upload_video(video_file):
35
  if video_file is None:
36
  return None, "μ—…λ‘œλ“œλœ 파일이 μ—†μŠ΅λ‹ˆλ‹€.", None, log_step("[단계 1] μ—…λ‘œλ“œλœ 파일이 μ—†μŠ΅λ‹ˆλ‹€.")
 
37
  temp_dir = tempfile.mkdtemp()
38
  temp_video_path = os.path.join(temp_dir, video_file.name)
39
  with open(temp_video_path, "wb") as f:
40
  f.write(video_file.read())
 
41
  try:
42
  clip = me.VideoFileClip(temp_video_path)
43
  duration_sec = clip.duration
44
  msg = f"μž¬μƒ μ‹œκ°„: {duration_sec:.2f}초"
45
+ log_out = log_step("[단계 1] λΉ„λ””μ˜€ μ—…λ‘œλ“œ 및 μž¬μƒ 확인 μ™„λ£Œ")
46
+ return temp_video_path, msg, duration_sec, log_out
 
 
47
  except Exception as e:
48
+ err_log = log_step(f"[단계 1] μ—…λ‘œλ“œ μ—λŸ¬: {e}")
49
+ return None, f"μ˜μƒ 처리 μ—λŸ¬: {e}", None, err_log
50
 
 
 
 
51
  def generate_thumbnails(video_path, start_time_str, end_time_str, log_history):
52
  log_step("[단계 4] μ‹œμž‘/끝 μ‹œκ°„ μž…λ ₯ μ™„λ£Œ")
53
  start_s = parse_time_str(start_time_str)
54
  end_s = parse_time_str(end_time_str)
55
+
56
  if not video_path:
57
+ return None, None, log_step("[단계 5] λΉ„λ””μ˜€ λ―Έμ—…λ‘œλ“œ β†’ 썸넀일 λΆˆκ°€")
58
 
59
  try:
60
  clip = me.VideoFileClip(video_path)
61
+ duration = clip.duration
62
  if start_s < 0: start_s = 0
63
+ if end_s > duration: end_s = duration
64
  if start_s > end_s: start_s, end_s = end_s, start_s
65
+
66
+ thumb1 = clip.get_frame(start_s)
67
+ thumb2 = clip.get_frame(end_s)
68
+
69
+ thumb1_img = Image.fromarray(thumb1)
70
+ thumb2_img = Image.fromarray(thumb2)
71
+
72
+ log_out = log_step("[단계 5] 썸넀일 생성 μ™„λ£Œ")
73
+ return thumb1_img, thumb2_img, log_out
74
  except Exception as e:
75
+ err_log = log_step(f"[단계 5] 썸넀일 생성 μ—λŸ¬: {e}")
76
+ return None, None, err_log
77
 
 
 
 
78
  def create_gif(video_path, start_time_str, end_time_str, resolution, fps, speed, loop_count, log_history):
79
  if not video_path:
80
  return None, None, log_step("[단계 10] GIF 생성 μ‹€νŒ¨: λΉ„λ””μ˜€κ°€ μ—…λ‘œλ“œλ˜μ§€ μ•ŠμŒ.")
81
 
82
  start_s = parse_time_str(start_time_str)
83
  end_s = parse_time_str(end_time_str)
84
+
85
  try:
86
  clip = me.VideoFileClip(video_path)
87
+ duration = clip.duration
88
  if start_s < 0: start_s = 0
89
+ if end_s > duration: end_s = duration
90
  if start_s > end_s: start_s, end_s = end_s, start_s
91
+
 
92
  subclip = clip.subclip(start_s, end_s)
 
 
93
  if speed != 1.0:
94
  subclip = subclip.fx(me.vfx.speedx, speed)
95
+
96
+ # 해상도 쑰절(κΈ°λ³Έ 100%)
97
+ w, h = subclip.size
98
  if resolution < 100:
 
99
  scale = resolution / 100.0
100
+ subclip = subclip.resize((int(w * scale), int(h * scale)))
101
+
102
+ # FPS
103
  fps = int(fps) if fps > 0 else None
104
+
105
+ # 반볡 횟수
106
+ loop = loop_count # 0=λ¬΄ν•œ
107
+
108
  temp_dir = tempfile.mkdtemp()
109
  gif_path = os.path.join(temp_dir, "output.gif")
110
+
111
  subclip.write_gif(gif_path, fps=fps, loop=loop)
112
+
113
+ log_out1 = log_step("[단계 10] 'GIF 생성' λ²„νŠΌ 클릭됨")
114
+ log_out2 = log_step("[단계 11] GIF 미리보기 생성 μ™„λ£Œ")
115
+ log_out3 = log_step("[단계 12] GIF λ‹€μš΄λ‘œλ“œ μ€€λΉ„ μ™„λ£Œ")
116
+
117
  preview_img = Image.open(gif_path)
118
+ return preview_img, gif_path, "\n".join([log_out1, log_out2, log_out3])
 
119
  except Exception as e:
120
+ err_log = log_step(f"[단계 10~12] GIF 생성 μ—λŸ¬: {e}")
121
+ return None, None, err_log
122
 
 
 
 
123
  def build_interface():
124
  with gr.Blocks() as demo:
125
  gr.Markdown("## λ™μ˜μƒμ„ GIF둜 λ³€ν™˜ν•˜κΈ°")
126
+
 
127
  video_upload = gr.File(label="λΉ„λ””μ˜€ μ—…λ‘œλ“œ", file_types=["video"])
 
 
128
  video_player = gr.Video(label="μ—…λ‘œλ“œλœ μ˜μƒ 미리보기")
129
+ video_info = gr.Textbox(label="μ—…λ‘œλ“œλœ μ˜μƒ 정보", interactive=False)
130
+
 
 
 
131
  video_path_state = gr.State()
132
  video_duration_state = gr.State()
133
+
 
134
  log_box = gr.Textbox(label="둜그", interactive=False, lines=10)
135
+
 
136
  with gr.Row():
137
+ start_time = gr.Textbox(label="μ‹œμž‘ μ‹œκ°„ (HH:MM:SS/ MM:SS)", value="0:00")
138
+ end_time = gr.Textbox(label="끝 μ‹œκ°„ (HH:MM:SS/ MM:SS)", value="0:10")
139
+
 
140
  with gr.Row():
141
  thumb1 = gr.Image(label="μ‹œμž‘ 지점 썸넀일")
142
  thumb2 = gr.Image(label="끝 지점 썸넀일")
143
+
144
+ resolution_slider = gr.Slider(1, 100, 100, step=1, label="해상도(%)")
145
+ fps_slider = gr.Slider(1, 60, 30, step=1, label="FPS")
146
+ speed_slider = gr.Slider(0.1, 3.0, 1.0, step=0.1, label="μž¬μƒ 속도(λ°°)")
147
+ loop_slider = gr.Slider(0, 10, 0, step=1, label="GIF 반볡 횟수 (0=λ¬΄ν•œ)")
148
+
149
+ generate_gif_btn = gr.Button("GIF 생성")
150
+
151
+ gif_preview = gr.Image(label="GIF 미리보기")
 
 
 
 
 
 
 
 
 
152
  gif_download = gr.File(label="GIF λ‹€μš΄λ‘œλ“œ(ν΄λ¦­ν•˜μ—¬ μ €μž₯)")
153
+
154
+ # 이벀트 바인딩
 
 
 
 
155
  video_upload.change(
156
  fn=upload_video,
157
  inputs=video_upload,
158
  outputs=[video_path_state, video_info, video_duration_state, log_box]
159
  )
 
 
160
  start_time.change(
161
  fn=generate_thumbnails,
162
  inputs=[video_path_state, start_time, end_time, log_box],
 
167
  inputs=[video_path_state, start_time, end_time, log_box],
168
  outputs=[thumb1, thumb2, log_box]
169
  )
 
 
170
  generate_gif_btn.click(
171
  fn=create_gif,
172
+ inputs=[video_path_state, start_time, end_time, resolution_slider, fps_slider, speed_slider, loop_slider, log_box],
 
 
 
 
 
 
 
 
 
173
  outputs=[gif_preview, gif_download, log_box]
174
  )
175
+
176
  return demo
177
 
178
  if __name__ == "__main__":