Kims12 commited on
Commit
ae62c0c
ยท
verified ยท
1 Parent(s): 726d6f9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +744 -61
app.py CHANGED
@@ -107,6 +107,8 @@ def process_video(video,
107
  return output_filename, output_filename, "\n".join(global_logs)
108
 
109
  def update_thumbnails(video, start_time_str, end_time_str):
 
 
110
  video_path = video if isinstance(video, str) else video.name
111
  try:
112
  input_video = mp.VideoFileClip(video_path)
@@ -126,120 +128,801 @@ def update_thumbnails(video, start_time_str, end_time_str):
126
  end_thumb = generate_thumbnail(input_video, end_sec)
127
  return start_thumb, end_thumb
128
 
 
 
 
 
 
 
 
 
 
 
 
 
129
  # -------------------------------
130
- # Custom CSS (๊ธ€์ž ํฌ๊ธฐ ํฌ๊ฒŒ, ํšŒ์ƒ‰ ์ œ๊ฑฐ, ํฌ์ธํŠธ ๋ฒ„ํŠผ ๋“ฑ)
131
  # -------------------------------
132
  custom_css = """
 
133
  body {
134
- font-size: 1.6em; /* ๊ธ€์ž ํฌ๊ธฐ ์ฆ๊ฐ€ */
135
- background-color: #ffffff;
136
- font-family: 'Arial', sans-serif;
 
137
  }
 
138
  .gradio-container {
139
- width: 80% !important;
 
140
  margin: 0 auto;
141
- background-color: #ffffff; /* ํšŒ์ƒ‰ ์ œ๊ฑฐ */
 
 
 
 
 
 
 
 
 
 
142
  }
143
- /* ์ œ๋ชฉ ๋ฐ ์‚ฌ์šฉ๊ฐ€์ด๋“œ (์™ผ์ชฝ ์ •๋ ฌ, ํฌ๊ฒŒ) */
144
  .custom-title {
145
- font-size: 3.5em; /* ๊ธ€์ž ํฌ๊ธฐ ์ฆ๊ฐ€ */
146
- font-weight: bold;
147
- margin: 20px 0;
148
- color: #2c3e50;
149
  text-align: left;
 
 
 
 
150
  }
151
- .custom-user-guide {
152
- font-size: 1.8em; /* ๊ธ€์ž ํฌ๊ธฐ ์ฆ๊ฐ€ */
153
- margin-bottom: 20px;
154
- color: #2c3e50;
155
- text-align: left;
 
156
  }
157
- /* ์‚ฌ์šฉ๊ฐ€์ด๋“œ ๋ฐ•์Šค ์Šคํƒ€์ผ (ํšŒ์ƒ‰ ์ œ๊ฑฐ, ํฐ์ƒ‰ ๋ฐฐ๊ฒฝ) */
 
158
  .guide-box {
159
- border: 3px solid #3498db; /* ํ…Œ๋‘๋ฆฌ ๋‘๊ป˜ ์ฆ๊ฐ€ */
160
- border-radius: 10px;
161
- padding: 20px; /* ํŒจ๋”ฉ ์ฆ๊ฐ€ */
162
- background-color: #ffffff;
163
- margin: 20px 0;
164
  text-align: left;
165
- font-size: 1.2em; /* ๊ธ€์ž ํฌ๊ธฐ ์ฆ๊ฐ€ */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
166
  }
 
 
 
 
 
 
 
 
 
 
 
 
167
  /* ํ”„๋ ˆ์ž„ ์Šคํƒ€์ผ */
168
  .frame {
169
- border: 3px solid #3498db; /* ํ…Œ๋‘๋ฆฌ ๋‘๊ป˜ ์ฆ๊ฐ€ */
170
- border-radius: 20px;
171
- padding: 25px; /* ํŒจ๋”ฉ ์ฆ๊ฐ€ */
172
  background-color: #ffffff;
173
  margin: 15px;
174
- box-shadow: 3px 3px 10px rgba(0,0,0,0.1);
 
175
  }
176
- /* ๊ฐ ํ”„๋ ˆ์ž„ ์ œ๋ชฉ ํฌ๊ฒŒ */
 
 
 
 
 
 
177
  .frame h3 {
178
- font-size: 2.2em; /* ๊ธ€์ž ํฌ๊ธฐ ์ฆ๊ฐ€ */
179
- margin-bottom: 20px;
 
180
  text-align: left;
 
 
 
 
 
 
 
 
 
181
  }
 
182
  /* ํ–‰ ๋ฐ ์ปฌ๋Ÿผ ๋ ˆ์ด์•„์›ƒ */
183
  .row-container {
184
  display: flex;
185
  justify-content: space-between;
 
 
186
  }
 
187
  .column {
188
  flex: 1;
189
- margin: 10px;
190
  }
 
191
  /* ํฌ์ธํŠธ ๋ฒ„ํŠผ ์Šคํƒ€์ผ */
192
  .gif-button {
193
  margin-top: 35px;
194
- padding: 20px 35px; /* ํŒจ๋”ฉ ์ฆ๊ฐ€ */
195
- font-size: 1.8em; /* ๊ธ€์ž ํฌ๊ธฐ ์ฆ๊ฐ€ */
196
- background-color: #e67e22;
197
  color: #fff;
198
  border: none;
199
  border-radius: 12px;
200
  cursor: pointer;
 
 
 
 
 
 
 
 
 
201
  }
202
- /* ์Šฌ๋ผ์ด๋” ์Šคํƒ€์ผ (๊ธ€์ž์™€ ๋ฐ” ํฌ๊ฒŒ) */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
203
  input[type=range] {
204
- height: 30px; /* ๋†’์ด ์ฆ๊ฐ€ */
 
 
 
 
 
 
 
 
 
 
 
 
205
  }
 
206
  input[type=range]::-webkit-slider-thumb {
207
- height: 30px; /* ๋†’์ด ์ฆ๊ฐ€ */
208
- width: 30px; /* ๋„ˆ๋น„ ์ฆ๊ฐ€ */
 
 
 
 
 
 
 
 
 
 
209
  }
 
210
  .slider-label {
211
- font-size: 1.6em; /* ๊ธ€์ž ํฌ๊ธฐ ์ฆ๊ฐ€ */
212
- margin-bottom: 8px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213
  }
214
- /* ๋ชจ๋“  input, select, button ๊ธ€์ž ํฌ๊ธฐ ํ‚ค์›€ */
215
- input, button, select, textarea {
216
- font-size: 1.5em; /* ๊ธ€์ž ํฌ๊ธฐ ์ฆ๊ฐ€ */
 
 
217
  }
218
- /* ๋ผ๋ฒจ ๊ธ€์ž ํฌ๊ธฐ ํ‚ค์›€ */
 
219
  label, .label-wrap span {
220
- font-size: 1.3em !important; /* ๊ธ€์ž ํฌ๊ธฐ ์ฆ๊ฐ€ */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
221
  font-weight: bold;
 
 
 
222
  }
223
- /* ํšŒ์ƒ‰ ๋ฐฐ๊ฒฝ ์ œ๊ฑฐ */
224
- .gradio-container .prose,
225
- .gradio-container .gr-box,
226
- .gradio-container .gr-form,
227
- .gradio-container .gr-panel {
228
- background-color: #ffffff !important;
 
 
 
229
  }
230
- .gradio-container .gr-form,
231
- .gradio-container .gr-group {
232
- border-color: #3498db;
233
- background-color: #ffffff !important;
 
234
  }
235
- .gradio-container .gr-input,
236
- .gradio-container .gr-checkbox,
237
- .gradio-container .gr-radio,
238
- .gradio-container .gr-dropdown {
239
- font-size: 1.4em; /* ๊ธ€์ž ํฌ๊ธฐ ์ฆ๊ฐ€ */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
240
  }
241
- """
242
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
243
  # -------------------------------
244
  # Gradio UI ๊ตฌ์„ฑ (HTML/CSS ์ปค์Šคํ„ฐ๋งˆ์ด์ง•)
245
  # -------------------------------
 
107
  return output_filename, output_filename, "\n".join(global_logs)
108
 
109
  def update_thumbnails(video, start_time_str, end_time_str):
110
+ if not video:
111
+ return None, None
112
  video_path = video if isinstance(video, str) else video.name
113
  try:
114
  input_video = mp.VideoFileClip(video_path)
 
128
  end_thumb = generate_thumbnail(input_video, end_sec)
129
  return start_thumb, end_thumb
130
 
131
+ def validate_input(video, start_time_str, end_time_str):
132
+ if not video:
133
+ return "โš ๏ธ ๋จผ์ € ์˜์ƒ์„ ์—…๋กœ๋“œํ•ด์ฃผ์„ธ์š”."
134
+ try:
135
+ start_sec = parse_time_to_seconds(start_time_str)
136
+ end_sec = parse_time_to_seconds(end_time_str)
137
+ if start_sec >= end_sec:
138
+ return "โš ๏ธ ์ข…๋ฃŒ ์‹œ๊ฐ„์€ ์‹œ์ž‘ ์‹œ๊ฐ„๋ณด๋‹ค ์ปค์•ผ ํ•ฉ๋‹ˆ๋‹ค."
139
+ except:
140
+ return "โš ๏ธ ์‹œ๊ฐ„ ํ˜•์‹์ด ์˜ฌ๋ฐ”๋ฅด์ง€ ์•Š์Šต๋‹ˆ๋‹ค. (์˜ˆ: 00:00:10)"
141
+ return None
142
+
143
  # -------------------------------
144
+ # Custom CSS (ํ–ฅ์ƒ๋œ ๋””์ž์ธ)
145
  # -------------------------------
146
  custom_css = """
147
+ /* ๊ธฐ๋ณธ ์Šคํƒ€์ผ */
148
  body {
149
+ font-size: 1.6em;
150
+ background-color: #f9f9f9;
151
+ font-family: 'Pretendard', 'Noto Sans KR', sans-serif;
152
+ color: #333333;
153
  }
154
+
155
  .gradio-container {
156
+ width: 90% !important;
157
+ max-width: 1200px !important;
158
  margin: 0 auto;
159
+ background-color: #ffffff;
160
+ border-radius: 15px;
161
+ box-shadow: 0 5px 30px rgba(0, 0, 0, 0.05);
162
+ padding: 20px;
163
+ }
164
+
165
+ /* ์•ฑ ํ—ค๋” */
166
+ .app-header {
167
+ border-bottom: 2px solid #f0f0f0;
168
+ margin-bottom: 25px;
169
+ padding-bottom: 15px;
170
  }
171
+
172
  .custom-title {
173
+ font-size: 3.2em;
174
+ font-weight: 800;
175
+ margin: 20px 0 15px 0;
176
+ color: #1e88e5;
177
  text-align: left;
178
+ line-height: 1.2;
179
+ letter-spacing: -0.5px;
180
+ padding-left: 10px;
181
+ border-left: 8px solid #1e88e5;
182
  }
183
+
184
+ .custom-subtitle {
185
+ font-size: 1.4em;
186
+ color: #555;
187
+ margin-bottom: 25px;
188
+ padding-left: 18px;
189
  }
190
+
191
+ /* ์‚ฌ์šฉ๊ฐ€์ด๋“œ ๋ฐ•์Šค */
192
  .guide-box {
193
+ border: none;
194
+ border-radius: 12px;
195
+ padding: 25px;
196
+ background-color: #e3f2fd;
197
+ margin: 20px 0 30px 0;
198
  text-align: left;
199
+ font-size: 1.2em;
200
+ line-height: 1.6;
201
+ box-shadow: 0 3px 10px rgba(0, 0, 0, 0.05);
202
+ }
203
+
204
+ .guide-box strong {
205
+ font-size: 1.3em;
206
+ color: #1e88e5;
207
+ display: block;
208
+ margin-bottom: 10px;
209
+ }
210
+
211
+ .guide-step {
212
+ margin: 10px 0;
213
+ padding-left: 15px;
214
+ position: relative;
215
  }
216
+
217
+ .guide-step:before {
218
+ content: '';
219
+ position: absolute;
220
+ left: 0;
221
+ top: 10px;
222
+ width: 8px;
223
+ height: 8px;
224
+ background-color: #1e88e5;
225
+ border-radius: 50%;
226
+ }
227
+
228
  /* ํ”„๋ ˆ์ž„ ์Šคํƒ€์ผ */
229
  .frame {
230
+ border: none;
231
+ border-radius: 15px;
232
+ padding: 30px;
233
  background-color: #ffffff;
234
  margin: 15px;
235
+ box-shadow: 0 5px 20px rgba(0, 0, 0, 0.08);
236
+ transition: all 0.3s ease;
237
  }
238
+
239
+ .frame:hover {
240
+ box-shadow: 0 8px 25px rgba(0, 0, 0, 0.12);
241
+ transform: translateY(-2px);
242
+ }
243
+
244
+ /* ํ”„๋ ˆ์ž„ ์ œ๋ชฉ */
245
  .frame h3 {
246
+ font-size: 2.0em;
247
+ font-weight: 700;
248
+ margin-bottom: 25px;
249
  text-align: left;
250
+ color: #1e88e5;
251
+ border-bottom: 2px solid #e3f2fd;
252
+ padding-bottom: 10px;
253
+ display: flex;
254
+ align-items: center;
255
+ }
256
+
257
+ .frame h3 span {
258
+ margin-left: 10px;
259
  }
260
+
261
  /* ํ–‰ ๋ฐ ์ปฌ๋Ÿผ ๋ ˆ์ด์•„์›ƒ */
262
  .row-container {
263
  display: flex;
264
  justify-content: space-between;
265
+ flex-wrap: wrap;
266
+ gap: 20px;
267
  }
268
+
269
  .column {
270
  flex: 1;
271
+ min-width: 45%;
272
  }
273
+
274
  /* ํฌ์ธํŠธ ๋ฒ„ํŠผ ์Šคํƒ€์ผ */
275
  .gif-button {
276
  margin-top: 35px;
277
+ padding: 20px 35px;
278
+ font-size: 1.8em;
279
+ background: linear-gradient(45deg, #1e88e5, #039be5);
280
  color: #fff;
281
  border: none;
282
  border-radius: 12px;
283
  cursor: pointer;
284
+ box-shadow: 0 4px 15px rgba(30, 136, 229, 0.3);
285
+ transition: all 0.3s ease;
286
+ font-weight: bold;
287
+ text-align: center;
288
+ width: 100%;
289
+ max-width: 500px;
290
+ margin-left: auto;
291
+ margin-right: auto;
292
+ display: block;
293
  }
294
+
295
+ .gif-button:hover {
296
+ box-shadow: 0 6px 20px rgba(30, 136, 229, 0.5);
297
+ transform: translateY(-2px);
298
+ }
299
+
300
+ .gif-button:active {
301
+ transform: translateY(1px);
302
+ box-shadow: 0 2px 10px rgba(30, 136, 229, 0.3);
303
+ }
304
+
305
+ /* ์ž…๋ ฅ ์š”์†Œ ์Šคํƒ€์ผ */
306
+ .input-element {
307
+ margin-bottom: 20px;
308
+ }
309
+
310
+ /* ๋ผ๋””์˜ค ๋ฒ„ํŠผ ์Šคํƒ€์ผ */
311
+ .radio-group label {
312
+ font-size: 1.3em;
313
+ padding: 10px 15px;
314
+ background-color: #f1f8fe;
315
+ border-radius: 8px;
316
+ margin: 5px;
317
+ transition: all 0.2s ease;
318
+ }
319
+
320
+ .radio-group input:checked + label {
321
+ background-color: #1e88e5;
322
+ color: white;
323
+ }
324
+
325
+ /* ์Šฌ๋ผ์ด๋” ์Šคํƒ€์ผ */
326
  input[type=range] {
327
+ height: 30px;
328
+ -webkit-appearance: none;
329
+ margin: 10px 0;
330
+ width: 100%;
331
+ background: transparent;
332
+ }
333
+
334
+ input[type=range]::-webkit-slider-runnable-track {
335
+ width: 100%;
336
+ height: 12px;
337
+ cursor: pointer;
338
+ background: #e3f2fd;
339
+ border-radius: 10px;
340
  }
341
+
342
  input[type=range]::-webkit-slider-thumb {
343
+ height: 30px;
344
+ width: 30px;
345
+ border-radius: 50%;
346
+ background: #1e88e5;
347
+ cursor: pointer;
348
+ -webkit-appearance: none;
349
+ margin-top: -9px;
350
+ box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
351
+ }
352
+
353
+ .slider-container {
354
+ margin: 25px 0;
355
  }
356
+
357
  .slider-label {
358
+ font-size: 1.5em;
359
+ margin-bottom: 10px;
360
+ color: #333;
361
+ font-weight: 600;
362
+ }
363
+
364
+ .slider-value {
365
+ font-size: 1.3em;
366
+ color: #1e88e5;
367
+ font-weight: bold;
368
+ text-align: right;
369
+ }
370
+
371
+ /* ์ž…๋ ฅ ํ•„๋“œ ์Šคํƒ€์ผ */
372
+ input[type=text], input[type=number], select, textarea {
373
+ font-size: 1.5em;
374
+ padding: 15px;
375
+ border-radius: 8px;
376
+ border: 2px solid #e3f2fd;
377
+ transition: all 0.3s ease;
378
+ width: 100%;
379
+ box-sizing: border-box;
380
+ margin-top: 5px;
381
  }
382
+
383
+ input[type=text]:focus, input[type=number]:focus, select:focus, textarea:focus {
384
+ border-color: #1e88e5;
385
+ outline: none;
386
+ box-shadow: 0 0 0 3px rgba(30, 136, 229, 0.2);
387
  }
388
+
389
+ /* ๋ผ๋ฒจ ์Šคํƒ€์ผ */
390
  label, .label-wrap span {
391
+ font-size: 1.4em !important;
392
+ font-weight: 600;
393
+ color: #333;
394
+ margin-bottom: 8px;
395
+ display: block;
396
+ }
397
+
398
+ /* ํ•„์ˆ˜ ํ‘œ์‹œ */
399
+ .required:after {
400
+ content: '*';
401
+ color: #e53935;
402
+ margin-left: 4px;
403
+ }
404
+
405
+ /* ์ธ๋„ค์ผ ์˜์—ญ ์Šคํƒ€์ผ */
406
+ .thumbnail-container {
407
+ display: flex;
408
+ flex-direction: column;
409
+ align-items: center;
410
+ justify-content: center;
411
+ min-height: 200px;
412
+ border: 2px dashed #e3f2fd;
413
+ border-radius: 10px;
414
+ padding: 15px;
415
+ background-color: #fafafa;
416
+ margin-top: 10px;
417
+ }
418
+
419
+ .thumbnail-container img {
420
+ max-width: 100%;
421
+ max-height: 100%;
422
+ object-fit: contain;
423
+ border-radius: 8px;
424
+ }
425
+
426
+ .thumbnail-label {
427
+ font-size: 1.2em;
428
  font-weight: bold;
429
+ margin-bottom: 10px;
430
+ color: #1e88e5;
431
+ text-align: center;
432
  }
433
+
434
+ /* ์ƒํƒœ ๋ฉ”์‹œ์ง€ */
435
+ .status-message {
436
+ padding: 15px;
437
+ border-radius: 8px;
438
+ margin: 20px 0;
439
+ font-size: 1.3em;
440
+ display: flex;
441
+ align-items: center;
442
  }
443
+
444
+ .status-message.success {
445
+ background-color: #e8f5e9;
446
+ color: #2e7d32;
447
+ border-left: 5px solid #2e7d32;
448
  }
449
+
450
+ .status-message.error {
451
+ background-color: #ffebee;
452
+ color: #c62828;
453
+ border-left: 5px solid #c62828;
454
+ }
455
+
456
+ .status-message.info {
457
+ background-color: #e3f2fd;
458
+ color: #1565c0;
459
+ border-left: 5px solid #1565c0;
460
+ }
461
+
462
+ .status-message i {
463
+ margin-right: 10px;
464
+ font-size: 1.5em;
465
+ }
466
+
467
+ /* ๋กœ๋”ฉ ์ƒํƒœ */
468
+ .loading {
469
+ display: inline-block;
470
+ position: relative;
471
+ width: 80px;
472
+ height: 80px;
473
+ }
474
+
475
+ .loading div {
476
+ position: absolute;
477
+ border: 4px solid #1e88e5;
478
+ opacity: 1;
479
+ border-radius: 50%;
480
+ animation: loading 1s cubic-bezier(0, 0.2, 0.8, 1) infinite;
481
+ }
482
+
483
+ .loading div:nth-child(2) {
484
+ animation-delay: -0.5s;
485
+ }
486
+
487
+ @keyframes loading {
488
+ 0% {
489
+ top: 36px;
490
+ left: 36px;
491
+ width: 0;
492
+ height: 0;
493
+ opacity: 1;
494
+ }
495
+ 100% {
496
+ top: 0px;
497
+ left: 0px;
498
+ width: 72px;
499
+ height: 72px;
500
+ opacity: 0;
501
+ }
502
+ }
503
+
504
+ /* ๋งˆ์šฐ์Šค ์˜ค๋ฒ„ ํšจ๊ณผ */
505
+ .hover-effect {
506
+ transition: all 0.3s ease;
507
+ }
508
+
509
+ .hover-effect:hover {
510
+ transform: translateY(-3px);
511
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
512
+ }
513
+
514
+ /* ๊ฒฐ๊ณผ ์˜์—ญ ์Šคํƒ€์ผ */
515
+ .result-container {
516
+ display: flex;
517
+ flex-direction: column;
518
+ align-items: center;
519
+ padding: 20px;
520
+ border-radius: 10px;
521
+ background-color: #f5f5f5;
522
+ margin-top: 20px;
523
+ }
524
+
525
+ .result-title {
526
+ font-size: 1.6em;
527
+ font-weight: bold;
528
+ margin-bottom: 15px;
529
+ color: #1e88e5;
530
+ }
531
+
532
+ .result-image {
533
+ max-width: 100%;
534
+ border-radius: 8px;
535
+ box-shadow: 0 3px 10px rgba(0, 0, 0, 0.1);
536
+ }
537
+
538
+ .download-button {
539
+ margin-top: 20px;
540
+ padding: 12px 25px;
541
+ background-color: #1e88e5;
542
+ color: white;
543
+ border: none;
544
+ border-radius: 8px;
545
+ font-size: 1.4em;
546
+ cursor: pointer;
547
+ transition: all 0.3s ease;
548
+ }
549
+
550
+ .download-button:hover {
551
+ background-color: #1565c0;
552
+ }
553
+
554
+ /* ์•Œ๋ฆผ ๋ฐฐ๋„ˆ ์Šคํƒ€์ผ */
555
+ .notification-banner {
556
+ padding: 15px 20px;
557
+ background-color: #fffde7;
558
+ border-left: 5px solid #fbc02d;
559
+ margin: 20px 0;
560
+ border-radius: 0 8px 8px 0;
561
+ font-size: 1.3em;
562
+ display: flex;
563
+ align-items: center;
564
+ }
565
+
566
+ .notification-banner i {
567
+ margin-right: 15px;
568
+ font-size: 1.6em;
569
+ color: #fbc02d;
570
+ }
571
+
572
+ /* ๋ฐ˜์‘ํ˜• ๋””์ž์ธ */
573
+ @media (max-width: 768px) {
574
+ .row-container {
575
+ flex-direction: column;
576
+ }
577
+
578
+ .column {
579
+ width: 100%;
580
+ }
581
+
582
+ .custom-title {
583
+ font-size: 2.5em;
584
+ }
585
+
586
+ .frame h3 {
587
+ font-size: 1.8em;
588
+ }
589
+
590
+ .gif-button {
591
+ font-size: 1.5em;
592
+ padding: 15px 25px;
593
+ }
594
  }
 
595
 
596
+ /* ํฌ์ปค์Šค ์ƒํƒœ ์‹œ๊ฐ ํšจ๊ณผ */
597
+ *:focus {
598
+ outline: none;
599
+ box-shadow: 0 0 0 3px rgba(30, 136, 229, 0.3);
600
+ }
601
+
602
+ /* ๋„์›€๋ง ํˆดํŒ */
603
+ .tooltip {
604
+ position: relative;
605
+ display: inline-block;
606
+ margin-left: 8px;
607
+ cursor: help;
608
+ }
609
+
610
+ .tooltip .tooltip-icon {
611
+ width: 20px;
612
+ height: 20px;
613
+ background-color: #1e88e5;
614
+ color: white;
615
+ border-radius: 50%;
616
+ display: flex;
617
+ align-items: center;
618
+ justify-content: center;
619
+ font-weight: bold;
620
+ font-size: 0.8em;
621
+ }
622
+
623
+ .tooltip .tooltip-text {
624
+ visibility: hidden;
625
+ width: 250px;
626
+ background-color: #555;
627
+ color: #fff;
628
+ text-align: center;
629
+ border-radius: 6px;
630
+ padding: 10px;
631
+ position: absolute;
632
+ z-index: 1;
633
+ bottom: 125%;
634
+ left: 50%;
635
+ margin-left: -125px;
636
+ opacity: 0;
637
+ transition: opacity 0.3s;
638
+ font-size: 0.9em;
639
+ }
640
+
641
+ .tooltip:hover .tooltip-text {
642
+ visibility: visible;
643
+ opacity: 1;
644
+ }
645
+
646
+ /* ์ž…์ถœ๋ ฅ ์˜์—ญ ๊ตฌ๋ถ„ */
647
+ .input-section, .output-section {
648
+ position: relative;
649
+ }
650
+
651
+ .section-label {
652
+ position: absolute;
653
+ top: -15px;
654
+ left: 30px;
655
+ padding: 5px 15px;
656
+ background-color: #1e88e5;
657
+ color: white;
658
+ font-size: 1.2em;
659
+ border-radius: 30px;
660
+ font-weight: bold;
661
+ z-index: 1;
662
+ }
663
+
664
+ /* ํŒŒ์ผ ์—…๋กœ๋“œ ์˜์—ญ ๊ฐœ์„  */
665
+ .file-upload {
666
+ border: 3px dashed #e3f2fd;
667
+ border-radius: 10px;
668
+ padding: 30px;
669
+ text-align: center;
670
+ cursor: pointer;
671
+ background-color: #f8fafc;
672
+ transition: all 0.3s ease;
673
+ }
674
+
675
+ .file-upload:hover {
676
+ border-color: #1e88e5;
677
+ background-color: #f0f7ff;
678
+ }
679
+
680
+ .file-upload-icon {
681
+ font-size: 3em;
682
+ color: #1e88e5;
683
+ margin-bottom: 15px;
684
+ }
685
+
686
+ .file-upload-text {
687
+ font-size: 1.3em;
688
+ color: #555;
689
+ }
690
+
691
+ /* ํ”„๋กœ๊ทธ๋ ˆ์Šค ๋ฐ” ์Šคํƒ€์ผ */
692
+ .progress-container {
693
+ width: 100%;
694
+ height: 15px;
695
+ background-color: #e0e0e0;
696
+ border-radius: 10px;
697
+ margin: 20px 0;
698
+ overflow: hidden;
699
+ }
700
+
701
+ .progress-bar {
702
+ height: 100%;
703
+ background: linear-gradient(90deg, #1e88e5, #29b6f6);
704
+ border-radius: 10px;
705
+ transition: width 0.5s ease;
706
+ }
707
+
708
+ /* ๋‹จ๊ณ„ ์ง„ํ–‰ ํ‘œ์‹œ๊ธฐ */
709
+ .steps-container {
710
+ display: flex;
711
+ justify-content: space-between;
712
+ margin: 30px 0;
713
+ }
714
+
715
+ .step {
716
+ display: flex;
717
+ flex-direction: column;
718
+ align-items: center;
719
+ width: 33%;
720
+ }
721
+
722
+ .step-circle {
723
+ width: 40px;
724
+ height: 40px;
725
+ border-radius: 50%;
726
+ background-color: #e0e0e0;
727
+ display: flex;
728
+ align-items: center;
729
+ justify-content: center;
730
+ font-weight: bold;
731
+ color: white;
732
+ margin-bottom: 10px;
733
+ }
734
+
735
+ .step.active .step-circle {
736
+ background-color: #1e88e5;
737
+ }
738
+
739
+ .step.completed .step-circle {
740
+ background-color: #43a047;
741
+ }
742
+
743
+ .step-label {
744
+ font-size: 1.2em;
745
+ text-align: center;
746
+ color: #555;
747
+ }
748
+
749
+ .step.active .step-label {
750
+ color: #1e88e5;
751
+ font-weight: bold;
752
+ }
753
+
754
+ .step-line {
755
+ flex-grow: 1;
756
+ height: 3px;
757
+ background-color: #e0e0e0;
758
+ margin: 20px 10px;
759
+ }
760
+
761
+ .step-line.completed {
762
+ background-color: #43a047;
763
+ }
764
+
765
+ /* ๋กœ๊ทธ ์ถœ๋ ฅ ์˜์—ญ */
766
+ .log-container {
767
+ padding: 15px;
768
+ background-color: #f5f5f5;
769
+ border-radius: 8px;
770
+ font-family: 'Courier New', monospace;
771
+ font-size: 1.2em;
772
+ max-height: 200px;
773
+ overflow-y: auto;
774
+ white-space: pre-wrap;
775
+ margin-top: 20px;
776
+ }
777
+
778
+ .log-entry {
779
+ margin: 5px 0;
780
+ padding: 3px 0;
781
+ border-bottom: 1px dashed #e0e0e0;
782
+ }
783
+
784
+ .log-entry.error {
785
+ color: #c62828;
786
+ }
787
+
788
+ .log-entry.success {
789
+ color: #2e7d32;
790
+ }
791
+
792
+ /* ์• ๋‹ˆ๋ฉ”์ด์…˜ ํšจ๊ณผ */
793
+ @keyframes fadeIn {
794
+ from { opacity: 0; transform: translateY(20px); }
795
+ to { opacity: 1; transform: translateY(0); }
796
+ }
797
+
798
+ .fade-in {
799
+ animation: fadeIn 0.5s ease forwards;
800
+ }
801
+
802
+ /* ์ฐจํŠธ ๋ฐ ํ†ต๊ณ„ ์˜์—ญ */
803
+ .stats-container {
804
+ display: flex;
805
+ flex-wrap: wrap;
806
+ gap: 15px;
807
+ margin-top: 20px;
808
+ }
809
+
810
+ .stat-card {
811
+ flex: 1;
812
+ min-width: 200px;
813
+ background-color: white;
814
+ border-radius: 10px;
815
+ padding: 20px;
816
+ box-shadow: 0 3px 10px rgba(0, 0, 0, 0.1);
817
+ text-align: center;
818
+ }
819
+
820
+ .stat-value {
821
+ font-size: 2em;
822
+ font-weight: bold;
823
+ color: #1e88e5;
824
+ margin: 10px 0;
825
+ }
826
+
827
+ .stat-label {
828
+ font-size: 1.2em;
829
+ color: #555;
830
+ }
831
+
832
+ /* ์ค‘์š” ์ •๋ณด ๊ฐ•์กฐ */
833
+ .highlight-text {
834
+ background-color: #fff9c4;
835
+ padding: 3px 5px;
836
+ border-radius: 3px;
837
+ font-weight: bold;
838
+ }
839
+
840
+ /* ๋ฒ„ํŠผ ๊ทธ๋ฃน ์Šคํƒ€์ผ */
841
+ .button-group {
842
+ display: flex;
843
+ gap: 10px;
844
+ margin: 20px 0;
845
+ }
846
+
847
+ .button-group button {
848
+ flex: 1;
849
+ padding: 12px;
850
+ border: none;
851
+ border-radius: 8px;
852
+ font-size: 1.3em;
853
+ cursor: pointer;
854
+ transition: all 0.3s ease;
855
+ }
856
+
857
+ .primary-button {
858
+ background-color: #1e88e5;
859
+ color: white;
860
+ }
861
+
862
+ .secondary-button {
863
+ background-color: #e0e0e0;
864
+ color: #333;
865
+ }
866
+
867
+ .danger-button {
868
+ background-color: #e53935;
869
+ color: white;
870
+ }
871
+
872
+ /* ์ž…๋ ฅ ์š”์†Œ ์˜ค๋ฅ˜ ์Šคํƒ€์ผ */
873
+ .input-error {
874
+ border-color: #e53935 !important;
875
+ }
876
+
877
+ .error-message {
878
+ color: #e53935;
879
+ font-size: 1.1em;
880
+ margin-top: 5px;
881
+ }
882
+
883
+ .gradio-app.dark {
884
+ background-color: #1e1e1e !important;
885
+ }
886
+
887
+ .gradio-app.dark .frame {
888
+ background-color: #2d2d2d !important;
889
+ box-shadow: 0 5px 20px rgba(0, 0, 0, 0.2) !important;
890
+ }
891
+
892
+ .gradio-app.dark label, .gradio-app.dark .label-wrap span {
893
+ color: #e0e0e0 !important;
894
+ }
895
+
896
+ .gradio-app.dark .guide-box {
897
+ background-color: #2d3748 !important;
898
+ }
899
+
900
+ .gradio-app.dark input[type=text],
901
+ .gradio-app.dark input[type=number],
902
+ .gradio-app.dark select,
903
+ .gradio-app.dark textarea {
904
+ background-color: #1e1e1e !important;
905
+ border-color: #3d3d3d !important;
906
+ color: #e0e0e0 !important;
907
+ }
908
+
909
+ .gradio-app.dark input[type=range]::-webkit-slider-runnable-track {
910
+ background-color: #3d3d3d !important;
911
+ }
912
+
913
+ .gradio-app.dark .custom-title,
914
+ .gradio-app.dark .frame h3 {
915
+ color: #64b5f6 !important;
916
+ }
917
+
918
+ .gradio-app.dark .gif-button {
919
+ background: linear-gradient(45deg, #1565c0, #0d47a1) !important;
920
+ }
921
+
922
+ .gradio-app.dark .slider-value {
923
+ color: #64b5f6 !important;
924
+ }
925
+ """
926
  # -------------------------------
927
  # Gradio UI ๊ตฌ์„ฑ (HTML/CSS ์ปค์Šคํ„ฐ๋งˆ์ด์ง•)
928
  # -------------------------------