mpdj4sd / app.py
ssboost's picture
Update app.py
81be558 verified
import gradio as gr
import os
import tempfile
from datetime import datetime
from PIL import Image
# ํ™˜๊ฒฝ๋ณ€์ˆ˜์—์„œ API ์—”๋“œํฌ์ธํŠธ ๊ฐ€์ ธ์˜ค๊ธฐ (์ฝ”๋“œ์—์„œ๋Š” ์ ˆ๋Œ€ ๋…ธ์ถœ ์•ˆ๋จ)
API_ENDPOINT = os.getenv('API_ENDPOINT')
if not API_ENDPOINT:
print("โŒ API_ENDPOINT ํ™˜๊ฒฝ๋ณ€์ˆ˜๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.")
exit(1)
# ๋กœ๊ทธ ์–ต์ œ๋œ ํด๋ผ์ด์–ธํŠธ ์ž„ํฌํŠธ
try:
from gradio_client import Client, handle_file
print("โœ… Gradio Client ์ž„ํฌํŠธ ์„ฑ๊ณต")
except ImportError as e:
print(f"โŒ Gradio Client ์ž„ํฌํŠธ ์˜ค๋ฅ˜: {e}")
exit(1)
# ์ปค์Šคํ…€ CSS ์Šคํƒ€์ผ (๊ธฐ์กด๊ณผ ๋™์ผ)
custom_css = """
/* ============================================
๋‹คํฌ๋ชจ๋“œ ์ž๋™ ๋ณ€๊ฒฝ ํ…œํ”Œ๋ฆฟ CSS
============================================ */
/* 1. CSS ๋ณ€์ˆ˜ ์ •์˜ (๋ผ์ดํŠธ๋ชจ๋“œ - ๊ธฐ๋ณธ๊ฐ’) */
:root {
/* ๋ฉ”์ธ ์ปฌ๋Ÿฌ */
--primary-color: #FB7F0D;
--secondary-color: #ff9a8b;
--accent-color: #FF6B6B;
/* ๋ฐฐ๊ฒฝ ์ปฌ๋Ÿฌ */
--background-color: #FFFFFF;
--card-bg: #ffffff;
--input-bg: #ffffff;
/* ํ…์ŠคํŠธ ์ปฌ๋Ÿฌ */
--text-color: #334155;
--text-secondary: #64748b;
/* ๋ณด๋” ๋ฐ ๊ตฌ๋ถ„์„  */
--border-color: #dddddd;
--border-light: #e5e5e5;
/* ํ…Œ์ด๋ธ” ์ปฌ๋Ÿฌ */
--table-even-bg: #f3f3f3;
--table-hover-bg: #f0f0f0;
/* ๊ทธ๋ฆผ์ž */
--shadow: 0 8px 30px rgba(251, 127, 13, 0.08);
--shadow-light: 0 2px 4px rgba(0, 0, 0, 0.1);
/* ๊ธฐํƒ€ */
--border-radius: 18px;
}
/* 2. ๋‹คํฌ๋ชจ๋“œ ์ƒ‰์ƒ ๋ณ€์ˆ˜ (์ž๋™ ๊ฐ์ง€) */
@media (prefers-color-scheme: dark) {
:root {
/* ๋ฐฐ๊ฒฝ ์ปฌ๋Ÿฌ */
--background-color: #1a1a1a;
--card-bg: #2d2d2d;
--input-bg: #2d2d2d;
/* ํ…์ŠคํŠธ ์ปฌ๋Ÿฌ */
--text-color: #e5e5e5;
--text-secondary: #a1a1aa;
/* ๋ณด๋” ๋ฐ ๊ตฌ๋ถ„์„  */
--border-color: #404040;
--border-light: #525252;
/* ํ…Œ์ด๋ธ” ์ปฌ๋Ÿฌ */
--table-even-bg: #333333;
--table-hover-bg: #404040;
/* ๊ทธ๋ฆผ์ž */
--shadow: 0 8px 30px rgba(0, 0, 0, 0.3);
--shadow-light: 0 2px 4px rgba(0, 0, 0, 0.2);
}
}
/* 3. ์ˆ˜๋™ ๋‹คํฌ๋ชจ๋“œ ํด๋ž˜์Šค (Gradio ํ† ๊ธ€์šฉ) */
[data-theme="dark"],
.dark,
.gr-theme-dark {
/* ๋ฐฐ๊ฒฝ ์ปฌ๋Ÿฌ */
--background-color: #1a1a1a;
--card-bg: #2d2d2d;
--input-bg: #2d2d2d;
/* ํ…์ŠคํŠธ ์ปฌ๋Ÿฌ */
--text-color: #e5e5e5;
--text-secondary: #a1a1aa;
/* ๋ณด๋” ๋ฐ ๊ตฌ๋ถ„์„  */
--border-color: #404040;
--border-light: #525252;
/* ํ…Œ์ด๋ธ” ์ปฌ๋Ÿฌ */
--table-even-bg: #333333;
--table-hover-bg: #404040;
/* ๊ทธ๋ฆผ์ž */
--shadow: 0 8px 30px rgba(0, 0, 0, 0.3);
--shadow-light: 0 2px 4px rgba(0, 0, 0, 0.2);
}
/* 4. ๊ธฐ๋ณธ ์š”์†Œ ๋‹คํฌ๋ชจ๋“œ ์ ์šฉ */
body {
font-family: 'Pretendard', 'Noto Sans KR', -apple-system, BlinkMacSystemFont, sans-serif;
background-color: var(--background-color) !important;
color: var(--text-color) !important;
line-height: 1.6;
margin: 0;
padding: 0;
font-size: 16px;
transition: background-color 0.3s ease, color 0.3s ease;
}
/* 5. Gradio ์ปจํ…Œ์ด๋„ˆ ๊ฐ•์ œ ์ ์šฉ */
.gradio-container,
.gradio-container *,
.gr-app,
.gr-app *,
.gr-interface {
background-color: var(--background-color) !important;
color: var(--text-color) !important;
}
/* ํ‘ธํ„ฐ ์ˆจ๊น€ ์„ค์ • ์ถ”๊ฐ€ */
footer {
visibility: hidden;
}
.gradio-container {
width: 100%;
margin: 0 auto;
padding: 20px;
background-color: var(--background-color);
}
/* โ”€โ”€ ๊ทธ๋ฃน ๋ž˜ํผ ๋ฐฐ๊ฒฝ ์™„์ „ ์ œ๊ฑฐ โ”€โ”€ */
.custom-section-group,
.gr-block.gr-group {
background-color: var(--background-color) !important;
box-shadow: none !important;
}
.custom-section-group::before,
.custom-section-group::after,
.gr-block.gr-group::before,
.gr-block.gr-group::after {
display: none !important;
content: none !important;
}
/* ๊ทธ๋ฃน ์ปจํ…Œ์ด๋„ˆ ๋ฐฐ๊ฒฝ์„ ์•„์ด๋ณด๋ฆฌ๋กœ, ๊ทธ๋ฆผ์ž ์ œ๊ฑฐ */
.custom-section-group {
background-color: var(--background-color) !important;
box-shadow: none !important;
}
/* 6. ์นด๋“œ ๋ฐ ํŒจ๋„ ์Šคํƒ€์ผ */
.custom-frame,
.gr-form,
.gr-box,
.gr-panel,
[class*="frame"],
[class*="card"],
[class*="panel"] {
background-color: var(--card-bg) !important;
border: 1px solid var(--border-color) !important;
border-radius: var(--border-radius);
padding: 20px;
margin: 10px 0;
box-shadow: var(--shadow) !important;
color: var(--text-color) !important;
}
/* ์„น์…˜ ๊ทธ๋ฃน ์Šคํƒ€์ผ - ํšŒ์ƒ‰ ๋ฐฐ๊ฒฝ ์™„์ „ ์ œ๊ฑฐ */
.custom-section-group {
margin-top: 20px;
padding: 0;
border: none;
border-radius: 0;
background-color: var(--background-color);
box-shadow: none !important;
}
/* ๋ฒ„ํŠผ ์Šคํƒ€์ผ - ๊ธ€์ž ํฌ๊ธฐ 18px */
.custom-button {
border-radius: 30px !important;
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)) !important;
color: white !important;
font-size: 18px !important;
padding: 10px 20px !important;
border: none;
box-shadow: 0 4px 8px rgba(251, 127, 13, 0.25);
transition: transform 0.3s ease;
}
.custom-button:hover {
transform: translateY(-2px);
box-shadow: 0 6px 12px rgba(251, 127, 13, 0.3);
}
/* ์ œ๋ชฉ ์Šคํƒ€์ผ (๋ชจ๋“  ํ•ญ๋ชฉ๋ช…์ด ๋™์ผํ•˜๊ฒŒ custom-title ํด๋ž˜์Šค๋กœ) */
.custom-title {
font-size: 28px;
font-weight: bold;
margin-bottom: 10px;
color: var(--text-color);
border-bottom: 2px solid var(--primary-color);
padding-bottom: 5px;
}
/* ์ด๋ฏธ์ง€ ์ปจํ…Œ์ด๋„ˆ - ํฌ๊ธฐ ๊ณ ์ • */
.image-container {
border-radius: var(--border-radius);
overflow: hidden;
border: 2px dashed var(--border-color);
transition: all 0.3s ease;
background-color: var(--card-bg);
}
/* ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ ์˜์—ญ ๊ฐœ์„  */
.gradio-container .gr-image {
border: 2px dashed var(--border-color) !important;
border-radius: var(--border-radius) !important;
background-color: var(--card-bg) !important;
transition: all 0.3s ease !important;
}
.gradio-container .gr-image:hover {
border-color: var(--primary-color) !important;
box-shadow: 0 4px 12px rgba(251, 127, 13, 0.15) !important;
}
/* ์—…๋กœ๋“œ ์˜์—ญ ๋‚ด๋ถ€ ํ…์ŠคํŠธ */
.gradio-container .gr-image .upload-container,
.gradio-container .gr-image [data-testid="upload-container"] {
background-color: var(--card-bg) !important;
color: var(--text-color) !important;
border: none !important;
}
/* ์—…๋กœ๋“œ ์˜์—ญ ๋“œ๋ž˜๊ทธ ์•ˆ๋‚ด ํ…์ŠคํŠธ */
.gradio-container .gr-image .upload-container p,
.gradio-container .gr-image [data-testid="upload-container"] p {
color: var(--text-color) !important;
font-size: 14px !important;
}
/* ์—…๋กœ๋“œ ๋ฒ„ํŠผ ์Šคํƒ€์ผ ๊ฐœ์„  */
.gradio-container .gr-image .upload-container button,
.gradio-container .gr-image [data-testid="upload-container"] button {
background-color: var(--primary-color) !important;
color: white !important;
border: none !important;
padding: 8px 16px !important;
border-radius: 8px !important;
font-size: 14px !important;
cursor: pointer !important;
transition: all 0.3s ease !important;
}
.gradio-container .gr-image .upload-container button:hover,
.gradio-container .gr-image [data-testid="upload-container"] button:hover {
background-color: var(--secondary-color) !important;
transform: translateY(-1px) !important;
}
/* ์—…๋กœ๋“œ ์˜์—ญ ์•„์ด์ฝ˜ */
.gradio-container .gr-image .upload-container svg,
.gradio-container .gr-image [data-testid="upload-container"] svg {
color: var(--primary-color) !important;
width: 32px !important;
height: 32px !important;
}
/* ์ด๋ฏธ์ง€๊ฐ€ ์—…๋กœ๋“œ๋œ ํ›„ ํ‘œ์‹œ ์˜์—ญ */
.gradio-container .gr-image img {
background-color: var(--card-bg) !important;
border-radius: var(--border-radius) !important;
}
/* ์ด๋ฏธ์ง€ ์ œ๊ฑฐ ๋ฒ„ํŠผ */
.gradio-container .gr-image .image-container button,
.gradio-container .gr-image [data-testid="image"] button {
background-color: rgba(255, 255, 255, 0.9) !important;
color: #333 !important;
border: none !important;
border-radius: 50% !important;
width: 28px !important;
height: 28px !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
cursor: pointer !important;
transition: all 0.3s ease !important;
}
.gradio-container .gr-image .image-container button:hover,
.gradio-container .gr-image [data-testid="image"] button:hover {
background-color: rgba(255, 255, 255, 1) !important;
transform: scale(1.1) !important;
}
/* ์—…๋กœ๋“œ ์ด๋ฏธ์ง€ ์ปจํ…Œ์ด๋„ˆ (600x600) */
.upload-image-container {
width: 600px !important;
height: 600px !important;
min-width: 600px !important;
min-height: 600px !important;
max-width: 600px !important;
max-height: 600px !important;
}
/* ์ถœ๋ ฅ ์ด๋ฏธ์ง€ ์ปจํ…Œ์ด๋„ˆ (700x600) */
.output-image-container {
width: 700px !important;
height: 600px !important;
min-width: 700px !important;
min-height: 600px !important;
max-width: 700px !important;
max-height: 600px !important;
}
.image-container:hover {
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
}
.image-container img {
width: 100% !important;
height: 100% !important;
object-fit: contain !important;
}
/* Gradio ์—…๋กœ๋“œ ์ด๋ฏธ์ง€ ์ปดํฌ๋„ŒํŠธ ํฌ๊ธฐ ๊ณ ์ • (600x600) */
.gradio-container .gr-image.upload-image {
width: 600px !important;
height: 600px !important;
min-width: 600px !important;
min-height: 600px !important;
max-width: 600px !important;
max-height: 600px !important;
}
/* Gradio ์ถœ๋ ฅ ์ด๋ฏธ์ง€ ์ปดํฌ๋„ŒํŠธ ํฌ๊ธฐ ๊ณ ์ • (700x600) */
.gradio-container .gr-image.output-image {
width: 700px !important;
height: 600px !important;
min-width: 700px !important;
min-height: 600px !important;
max-width: 700px !important;
max-height: 600px !important;
}
/* ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ ์˜์—ญ ํฌ๊ธฐ ๊ณ ์ • */
.gradio-container .gr-image.upload-image > div {
width: 600px !important;
height: 600px !important;
min-width: 600px !important;
min-height: 600px !important;
max-width: 600px !important;
max-height: 600px !important;
}
/* ์ด๋ฏธ์ง€ ์ถœ๋ ฅ ์˜์—ญ ํฌ๊ธฐ ๊ณ ์ • */
.gradio-container .gr-image.output-image > div {
width: 700px !important;
height: 600px !important;
min-width: 700px !important;
min-height: 600px !important;
max-width: 700px !important;
max-height: 600px !important;
}
/* ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ ๋“œ๋ž˜๊ทธ ์˜์—ญ ํฌ๊ธฐ ๊ณ ์ • */
.gradio-container .gr-image.upload-image .image-container,
.gradio-container .gr-image.upload-image [data-testid="image"],
.gradio-container .gr-image.upload-image .upload-container {
width: 600px !important;
height: 600px !important;
min-width: 600px !important;
min-height: 600px !important;
max-width: 600px !important;
max-height: 600px !important;
}
/* ์ด๋ฏธ์ง€ ์ถœ๋ ฅ ๋“œ๋ž˜๊ทธ ์˜์—ญ ํฌ๊ธฐ ๊ณ ์ • */
.gradio-container .gr-image.output-image .image-container,
.gradio-container .gr-image.output-image [data-testid="image"],
.gradio-container .gr-image.output-image .upload-container {
width: 700px !important;
height: 600px !important;
min-width: 700px !important;
min-height: 600px !important;
max-width: 700px !important;
max-height: 600px !important;
}
/* 7. ์ž…๋ ฅ ํ•„๋“œ ์Šคํƒ€์ผ */
.gr-input, .gr-text-input, .gr-sample-inputs,
input[type="text"],
input[type="number"],
input[type="email"],
input[type="password"],
textarea,
select,
.gr-textarea,
.gr-dropdown {
border-radius: var(--border-radius) !important;
border: 1px solid var(--border-color) !important;
padding: 14px !important;
font-size: 15px !important;
background-color: var(--input-bg) !important;
color: var(--text-color) !important;
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05) !important;
transition: all 0.3s ease !important;
}
.gr-input:focus, .gr-text-input:focus,
input[type="text"]:focus,
input[type="number"]:focus,
input[type="email"]:focus,
input[type="password"]:focus,
textarea:focus,
select:focus,
.gr-textarea:focus,
.gr-dropdown:focus {
border-color: var(--primary-color) !important;
outline: none !important;
box-shadow: 0 0 0 2px rgba(251, 127, 13, 0.2) !important;
}
/* 8. ๋ผ๋ฒจ ๋ฐ ํ…์ŠคํŠธ ์š”์†Œ */
.gradio-container label,
label,
.gr-label,
.gr-checkbox label,
.gr-radio label,
p, span, div {
color: var(--text-color) !important;
font-size: 16px !important;
font-weight: 600 !important;
margin-bottom: 8px !important;
}
/* ๋“œ๋กญ๋‹ค์šด ๋ฐ ๋ผ๋””์˜ค ๋ฒ„ํŠผ ํฐํŠธ ํฌ๊ธฐ */
.gr-radio label, .gr-dropdown label, .gr-checkbox label {
font-size: 15px !important;
}
/* ๋ผ๋””์˜ค ๋ฒ„ํŠผ ์„ ํƒ์ง€ ๋ณผ๋“œ ์ฒ˜๋ฆฌ ์ œ๊ฑฐ */
.gr-radio .gr-radio-option label,
.gr-radio input[type="radio"] + label,
.gr-radio .gr-form label {
font-weight: normal !important;
font-size: 15px !important;
}
/* ๋ผ๋””์˜ค ๋ฒ„ํŠผ ๊ทธ๋ฃน ๋‚ด ๋ชจ๋“  ๋ผ๋ฒจ ์ผ๋ฐ˜ ํฐํŠธ๋กœ ์„ค์ • */
.gr-radio fieldset label {
font-weight: normal !important;
}
/* ๋งˆํฌ๋‹ค์šด ํ…์ŠคํŠธ ํฌ๊ธฐ ์ฆ๊ฐ€ */
.gradio-container .gr-markdown {
font-size: 15px !important;
line-height: 1.6 !important;
color: var(--text-color) !important;
}
/* ํ…์ŠคํŠธ๋ฐ•์Šค ๋‚ด์šฉ ํฐํŠธ ํฌ๊ธฐ */
.gr-textbox textarea, .gr-textbox input {
font-size: 15px !important;
background-color: var(--input-bg) !important;
color: var(--text-color) !important;
}
/* ์•„์ฝ”๋””์–ธ ์ œ๋ชฉ ํฐํŠธ ํฌ๊ธฐ */
.gr-accordion summary {
font-size: 17px !important;
font-weight: 600 !important;
background-color: var(--card-bg) !important;
color: var(--text-color) !important;
}
/* ๋ฉ”์ธ ์ปจํ…์ธ  ์Šคํฌ๋กค๋ฐ” */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: var(--card-bg);
border-radius: 10px;
}
::-webkit-scrollbar-thumb {
background: var(--primary-color);
border-radius: 10px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--secondary-color);
}
/* ์• ๋‹ˆ๋ฉ”์ด์…˜ ์Šคํƒ€์ผ */
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.fade-in {
animation: fadeIn 0.5s ease-out;
}
/* ๋ฐ˜์‘ํ˜• */
@media (max-width: 768px) {
.button-grid {
grid-template-columns: repeat(2, 1fr);
}
}
/* ์„น์…˜ ์ œ๋ชฉ ์Šคํƒ€์ผ */
.section-title {
display: flex;
align-items: center;
font-size: 24px;
font-weight: 700;
color: var(--text-color);
margin-bottom: 15px;
padding-bottom: 8px;
border-bottom: 2px solid var(--primary-color);
font-family: 'Pretendard', 'Noto Sans KR', -apple-system, BlinkMacSystemFont, sans-serif;
}
.section-title img {
margin-right: 12px;
width: 28px;
height: 28px;
/* ๋‹คํฌ๋ชจ๋“œ์—์„œ ์•„์ด์ฝ˜ ํ•„ํ„ฐ ์ ์šฉ */
filter: brightness(0) saturate(100%) invert(27%) sepia(51%) saturate(2878%) hue-rotate(346deg) brightness(104%) contrast(97%);
}
/* ๋ผ์ดํŠธ๋ชจ๋“œ์—์„œ๋Š” ์›๋ž˜ ์•„์ด์ฝ˜ ์ƒ‰์ƒ ์œ ์ง€ */
@media (prefers-color-scheme: light) {
.section-title img {
filter: none;
}
}
/* ์ˆ˜๋™ ๋‹คํฌ๋ชจ๋“œ ํด๋ž˜์Šค์—์„œ๋„ ์•„์ด์ฝ˜ ์ƒ‰์ƒ ์ ์šฉ */
[data-theme="dark"] .section-title img,
.dark .section-title img,
.gr-theme-dark .section-title img {
filter: brightness(0) saturate(100%) invert(27%) sepia(51%) saturate(2878%) hue-rotate(346deg) brightness(104%) contrast(97%);
}
/* 10. ์•„์ฝ”๋””์–ธ ๋ฐ ๋“œ๋กญ๋‹ค์šด - ์ˆ˜๋™์„ค์ • ์˜์—ญ ํšŒ์ƒ‰ ๋ฐฐ๊ฒฝ ์ œ๊ฑฐ */
details,
.gr-accordion,
.gr-accordion details {
background-color: var(--background-color) !important;
border: 1px solid var(--border-color) !important;
color: var(--text-color) !important;
border-radius: var(--border-radius) !important;
margin: 10px 0 !important;
}
details summary,
.gr-accordion summary,
.gr-accordion details summary {
background-color: var(--card-bg) !important;
color: var(--text-color) !important;
padding: 12px 16px !important;
border-radius: var(--border-radius) !important;
cursor: pointer !important;
border: none !important;
font-weight: 600 !important;
transition: all 0.3s ease !important;
}
details summary:hover,
.gr-accordion summary:hover,
.gr-accordion details summary:hover {
background-color: var(--table-hover-bg) !important;
}
/* ์•„์ฝ”๋””์–ธ ๋‚ด๋ถ€ ์ฝ˜ํ…์ธ  */
details[open],
.gr-accordion[open],
.gr-accordion details[open] {
background-color: var(--background-color) !important;
}
details[open] > *:not(summary),
.gr-accordion[open] > *:not(summary),
.gr-accordion details[open] > *:not(summary) {
background-color: var(--background-color) !important;
color: var(--text-color) !important;
padding: 16px !important;
border-top: 1px solid var(--border-color) !important;
}
/* ๊ทธ๋ฃน ๋‚ด๋ถ€ ์Šคํƒ€์ผ - ์ˆ˜๋™์„ค์ • ๋‚ด๋ถ€ ๊ทธ๋ฃน๋“ค */
.gr-group,
details .gr-group,
.gr-accordion .gr-group {
background-color: var(--background-color) !important;
border: none !important;
padding: 12px 0 !important;
margin: 8px 0 !important;
border-radius: var(--border-radius) !important;
}
/* ๊ทธ๋ฃน ๋‚ด๋ถ€ ์ œ๋ชฉ */
.gr-group .gr-markdown h3,
.gr-group h3 {
color: var(--text-color) !important;
font-size: 16px !important;
font-weight: 600 !important;
margin-bottom: 12px !important;
padding-bottom: 6px !important;
border-bottom: 1px solid var(--border-color) !important;
}
/* 11. ์ถ”๊ฐ€ Gradio ์ปดํฌ๋„ŒํŠธ๋“ค */
.gr-block,
.gr-group,
.gr-row,
.gr-column {
background-color: var(--background-color) !important;
color: var(--text-color) !important;
}
/* 12. ๋ฒ„ํŠผ์€ ๊ธฐ์กด ์Šคํƒ€์ผ ์œ ์ง€ (primary-color ์‚ฌ์šฉ) */
button:not([class*="custom"]):not([class*="primary"]):not([class*="secondary"]) {
background-color: var(--card-bg) !important;
color: var(--text-color) !important;
border-color: var(--border-color) !important;
}
/* 13. ์ฝ”๋“œ ๋ธ”๋ก ๋ฐ pre ํƒœ๊ทธ */
code,
pre,
.code-block {
background-color: var(--table-even-bg) !important;
color: var(--text-color) !important;
border-color: var(--border-color) !important;
}
/* 14. ์ „ํ™˜ ์• ๋‹ˆ๋ฉ”์ด์…˜ */
* {
transition: background-color 0.3s ease,
color 0.3s ease,
border-color 0.3s ease !important;
}
"""
# FontAwesome ์•„์ด์ฝ˜ ํฌํ•จ
fontawesome_link = """
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" crossorigin="anonymous" referrerpolicy="no-referrer" />
"""
def create_download_filename(keyword):
"""ํ•œ๊ตญ์‹œ๊ฐ„ ๊ธฐ์ค€์œผ๋กœ ๋‹ค์šด๋กœ๋“œ ํŒŒ์ผ๋ช… ์ƒ์„ฑ"""
from datetime import datetime, timezone, timedelta
# ํ•œ๊ตญ ์‹œ๊ฐ„๋Œ€ ์„ค์ • (UTC+9)
kst = timezone(timedelta(hours=9))
now = datetime.now(kst)
# ํŒŒ์ผ๋ช…์— ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋Š” ๋ฌธ์ž ์ œ๊ฑฐ
import re
safe_keyword = re.sub(r'[<>:"/\\|?*]', '_', keyword) if keyword else "์ƒํ’ˆ"
safe_keyword = safe_keyword[:30] # ๊ธธ์ด ์ œํ•œ 30์ž๋กœ ๋ณ€๊ฒฝ
# YYMMDD_์‹œ๊ฐ„๋ถ„ ํ˜•์‹
time_str = now.strftime("%y%m%d_%H%M")
filename = f"{safe_keyword}_{time_str}.jpg"
return filename
def prepare_download_file(image, keyword):
"""๋‹ค์šด๋กœ๋“œ์šฉ ์ž„์‹œ ํŒŒ์ผ ์ƒ์„ฑ (์˜ฌ๋ฐ”๋ฅธ ํŒŒ์ผ๋ช… ํฌํ•จ)"""
if image is None:
return None
try:
# ํ•œ๊ตญ์‹œ๊ฐ„ ๊ธฐ์ค€ ํŒŒ์ผ๋ช… ์ƒ์„ฑ
filename = create_download_filename(keyword)
# ์ž„์‹œ ๋””๋ ‰ํ† ๋ฆฌ์— ์›ํ•˜๋Š” ํŒŒ์ผ๋ช…์œผ๋กœ ์ €์žฅ
import tempfile
import os
# ์ž„์‹œ ๋””๋ ‰ํ† ๋ฆฌ ์ƒ์„ฑ
temp_dir = tempfile.mkdtemp()
file_path = os.path.join(temp_dir, filename)
# PIL Image๋ฅผ RGB๋กœ ๋ณ€ํ™˜ ํ›„ ์ €์žฅ
if isinstance(image, Image.Image):
# RGBA๋ฅผ RGB๋กœ ๋ณ€ํ™˜
if image.mode == 'RGBA':
background = Image.new('RGB', image.size, (255, 255, 255))
background.paste(image, mask=image.split()[-1])
image_to_save = background
else:
image_to_save = image.convert('RGB')
else:
image_to_save = image
# JPG๋กœ ์ €์žฅ (์›ํ•˜๋Š” ํŒŒ์ผ๋ช…์œผ๋กœ)
image_to_save.save(file_path, 'JPEG', quality=95)
print(f"โœ… ๋‹ค์šด๋กœ๋“œ ํŒŒ์ผ ์ค€๋น„: {filename}")
return file_path
except Exception as e:
print(f"โŒ ๋‹ค์šด๋กœ๋“œ ํŒŒ์ผ ์ค€๋น„ ์‹คํŒจ: {e}")
return None
# ๐ŸŽฏ ํ•ต์‹ฌ: ํด๋ผ์ด์–ธํŠธ ์—ฐ๊ฒฐ ํ•จ์ˆ˜ (๋กœ๊ทธ ์ตœ์†Œํ™”)
def get_client():
"""ํ™˜๊ฒฝ๋ณ€์ˆ˜ ๊ธฐ๋ฐ˜ ํด๋ผ์ด์–ธํŠธ ์—ฐ๊ฒฐ"""
try:
client = Client(API_ENDPOINT)
return client
except Exception as e:
print(f"โŒ ์—ฐ๊ฒฐ ์‹คํŒจ: {str(e)[:50]}...")
return None
def main():
with gr.Blocks(
css=custom_css,
theme=gr.themes.Default(
primary_hue="orange",
secondary_hue="orange",
font=[gr.themes.GoogleFont("Noto Sans KR"), "ui-sans-serif", "system-ui"]
),
title="UHP ์ด๋ฏธ์ง€์ƒ์„ฑ๊ธฐ"
) as app:
# FontAwesome ๋งํฌ ์ถ”๊ฐ€
gr.HTML(fontawesome_link)
# ์ƒํƒœ ๊ด€๋ฆฌ
copy_suggestions_state = gr.State({})
current_keyword_state = gr.State("")
with gr.Row():
# ์™ผ์ชฝ: 1๋‹จ๊ณ„ AI ์นดํ”ผ ์ƒ์„ฑ
with gr.Column(scale=1):
# AI ์นดํ”ผ ์ƒ์„ฑ ์„น์…˜
with gr.Column(elem_classes="custom-frame"):
gr.HTML('<div class="section-title"><img src="https://cdn-icons-png.flaticon.com/512/3097/3097412.png"> 1๋‹จ๊ณ„: AI ์นดํ”ผ ์ƒ์„ฑ</div>')
product_keyword = gr.Textbox(
label="์ƒํ’ˆ ํ‚ค์›Œ๋“œ",
placeholder="์˜ˆ: ๋ฐ”๋‚˜๋ฐ”์žŽ ์ถ”์ถœ๋ฌผ, ํ”„๋ฆฌ๋ฏธ์—„ ํ…€๋ธ”๋Ÿฌ, ์ฒœ์—ฐ ์Šคํ‚จ์ผ€์–ด"
)
copy_type_selection = gr.Radio(
choices=[
"์žฅ์ ์š”์•ฝํ˜•", "๋ฌธ์ œ์ œ์‹œํ˜•", "์‚ฌํšŒ์ ์ฆ๊ฑฐํ˜•", "๊ธด๊ธ‰์„ฑ์œ ๋„ํ˜•",
"๊ฐ€๊ฒฉ๊ฒฝ์Ÿ๋ ฅํ˜•", "๋งค์ธ๋ณ€ํ™”ํ˜•", "์ถฉ๋™๊ตฌ๋งค์œ ๋„ํ˜•", "๊ณตํฌ์†Œ๊ตฌํ˜•"
],
label="๐Ÿ“‹ 1. ์นดํ”ผ ํƒ€์ž… ์„ ํƒ",
value="์žฅ์ ์š”์•ฝํ˜•",
visible=True
)
copy_type_description = gr.Markdown(
"**์žฅ์ ์š”์•ฝํ˜•**: ์ œํ’ˆ์˜ ์žฅ์ ์„ ํ•œ๋ˆˆ์— ๊ฐ•์กฐ - ํ•ต์‹ฌ ๊ธฐ๋Šฅ๊ณผ ํ˜œํƒ์„ ๊ฐ„๊ฒฐํ•˜๊ฒŒ ์š”์•ฝํ•˜์—ฌ ์ œ์‹œ",
visible=True
)
generate_copy_btn = gr.Button("๋ฉ”์ธ์นดํ”ผ ์ƒ์„ฑ", elem_classes="custom-button")
# ์นดํ”ผ ์ถœ๋ ฅ ์˜์—ญ
copy1_display = gr.Textbox(label="์ถ”์ฒœ1", interactive=False, show_label=True)
copy2_display = gr.Textbox(label="์ถ”์ฒœ2", interactive=False, show_label=True)
copy3_display = gr.Textbox(label="์ถ”์ฒœ3", interactive=False, show_label=True)
copy4_display = gr.Textbox(label="์ถ”์ฒœ4", interactive=False, show_label=True)
copy5_display = gr.Textbox(label="์ถ”์ฒœ5", interactive=False, show_label=True)
copy_selection = gr.Radio(
choices=["์ถ”์ฒœ 1", "์ถ”์ฒœ 2", "์ถ”์ฒœ 3", "์ถ”์ฒœ 4", "์ถ”์ฒœ 5"],
label="๐Ÿ“ 2. ์นดํ”ผ ์„ ํƒ",
value="์ถ”์ฒœ 1",
visible=True
)
with gr.Row():
main_text = gr.Textbox(
label="๋ฉ”์ธ์นดํ”ผ",
placeholder="์œ„์—์„œ ์นดํ”ผ๋ฅผ ์„ ํƒํ•˜๋ฉด ์ž๋™์œผ๋กœ ์ž…๋ ฅ๋ฉ๋‹ˆ๋‹ค",
interactive=True
)
sub_text = gr.Textbox(
label="์„œ๋ธŒ์นดํ”ผ",
placeholder="์œ„์—์„œ ์นดํ”ผ๋ฅผ ์„ ํƒํ•˜๋ฉด ์ž๋™์œผ๋กœ ์ž…๋ ฅ๋ฉ๋‹ˆ๋‹ค",
interactive=True
)
# ์˜ˆ์‹œ
gr.Examples(
examples=[
["ํ†ต๊ตฝ์Šฌ๋ฆฌํผ"], ["๋ฐ”๋‚˜๋ฐ”์žŽ ์ถ”์ถœ๋ฌผ"], ["ํ”„๋ฆฌ๋ฏธ์—„ ํ…€๋ธ”๋Ÿฌ"],
["์ฒœ์—ฐ ์Šคํ‚จ์ผ€์–ด"], ["์œ ๊ธฐ๋† ์›๋‘"], ["๋ฌด์„  ์ด์–ดํฐ"]
],
inputs=[product_keyword]
)
# ์˜ค๋ฅธ์ชฝ: 2๋‹จ๊ณ„ ์ด๋ฏธ์ง€์ƒ์„ฑ
with gr.Column(scale=1):
# ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ ๋ฐ ์„ค์ • ์„น์…˜
with gr.Column(elem_classes="custom-frame"):
gr.HTML('<div class="section-title"><img src="https://cdn-icons-png.flaticon.com/512/4297/4297825.png"> 2๋‹จ๊ณ„: ์ด๋ฏธ์ง€์ƒ์„ฑ</div>')
input_image = gr.Image(
type="filepath",
label="์ƒํ’ˆ ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ",
elem_classes="image-container upload-image-container"
)
color_mode = gr.Radio(
choices=["์ถ”์ฒœ๋ฐฐ๊ฒฝ", "ํฐ์ƒ‰๋ฐฐ๊ฒฝ", "์ˆ˜๋™ ์„ค์ •๋ฐฐ๊ฒฝ"],
value="์ถ”์ฒœ๋ฐฐ๊ฒฝ",
label="๋ฐฐ๊ฒฝ์ƒ‰ ์„ค์ •"
)
generate_image_btn = gr.Button("์ด๋ฏธ์ง€์ƒ์„ฑ", elem_classes="custom-button")
# ์ˆ˜๋™์„ค์ • ์•„์ฝ”๋””์–ธ
with gr.Accordion("์ˆ˜๋™์„ค์ •", open=False):
with gr.Group():
gr.Markdown("### ํฐํŠธ ์„ ํƒ")
with gr.Row():
main_font_choice = gr.Dropdown(
choices=[
"ํ”„๋ฆฌํ…๋‹ค๋“œ-Bold", "์—์Šค์ฝ”์–ด๋“œ๋ฆผ-Bold", "๋…ธํ† ์‚ฐ์Šค-Bold",
"๋ฐฐ๋‹ฌ์˜๋ฏผ์กฑ ๋„ํ˜„์ฒด", "๋ฐฐ๋‹ฌ์˜๋ฏผ์กฑ ์ฃผ์•„์ฒด", "์—ฌ๊ธฐ์–ด๋•Œ ์ž˜๋‚œ์ฒด",
"์˜จ๊ธ€์žŽ ์ฝ˜์ฝ˜์ฒด", "์˜จ๊ธ€์žŽ ๋ฐ•๋‹คํ˜„์ฒด", "์ด์‚ฌ๋งŒ๋ฃจ-Bold",
"์นดํŽ˜24 ์•„๋„ค๋ชจ๋„ค-Bold", "์–ด๊ทธ๋กœ์ฒด-Bold", "SFํ•จ๋ฐ•๋ˆˆ", "ํŽ˜์ดํผ๋กœ์ง€-Bold"
],
value="ํ”„๋ฆฌํ…๋‹ค๋“œ-Bold",
label="๋ฉ”์ธ ์นดํ”ผ ํฐํŠธ"
)
sub_font_choice = gr.Dropdown(
choices=[
"ํ”„๋ฆฌํ…๋‹ค๋“œ-Regular", "์—์Šค์ฝ”์–ด๋“œ๋ฆผ-Regular", "๋…ธํ† ์‚ฐ์Šค-Regular",
"๋ฐฐ๋‹ฌ์˜๋ฏผ์กฑ ๋„ํ˜„์ฒด", "๋ฐฐ๋‹ฌ์˜๋ฏผ์กฑ ์ฃผ์•„์ฒด", "์—ฌ๊ธฐ์–ด๋•Œ ์ž˜๋‚œ์ฒด",
"์˜จ๊ธ€์žŽ ์ฝ˜์ฝ˜์ฒด", "์˜จ๊ธ€์žŽ ๋ฐ•๋‹คํ˜„์ฒด", "์ด์‚ฌ๋งŒ๋ฃจ-Light",
"์นดํŽ˜24 ์•„๋„ค๋ชจ๋„ค-Regular", "์–ด๊ทธ๋กœ์ฒด-Light", "SFํ•จ๋ฐ•๋ˆˆ", "ํŽ˜์ดํผ๋กœ์ง€-Regular"
],
value="ํ”„๋ฆฌํ…๋‹ค๋“œ-Regular",
label="์„œ๋ธŒ ์นดํ”ผ ํฐํŠธ"
)
with gr.Group():
gr.Markdown("### ์ˆ˜๋™ ์ƒ‰์ƒ ์„ค์ •")
with gr.Row():
manual_bg_color = gr.ColorPicker(label="๋ฐฐ๊ฒฝ์ƒ‰", value="#FFFFFF", interactive=True)
with gr.Row():
manual_main_text_color = gr.ColorPicker(label="๋ฉ”์ธ ํ…์ŠคํŠธ์ƒ‰", value="#000000", interactive=True)
manual_sub_text_color = gr.ColorPicker(label="์„œ๋ธŒ ํ…์ŠคํŠธ์ƒ‰", value="#000000", interactive=True)
with gr.Group():
gr.Markdown("### ์ˆ˜๋™ ํฐํŠธ ํฌ๊ธฐ ์„ค์ •")
with gr.Row():
manual_main_font_size = gr.Slider(
minimum=20, maximum=200, value=100, step=5,
label="๋ฉ”์ธ ํฐํŠธ ํฌ๊ธฐ (px)", interactive=True
)
manual_sub_font_size = gr.Slider(
minimum=15, maximum=120, value=55, step=5,
label="์„œ๋ธŒ ํฐํŠธ ํฌ๊ธฐ (px)", interactive=True
)
with gr.Group():
gr.Markdown("### ๐Ÿ“ ์—ฌ๋ฐฑ ๋ฐ ๊ฐ„๊ฒฉ ์กฐ์ •")
with gr.Row():
top_bottom_margin = gr.Slider(
minimum=50, maximum=800, value=450, step=10,
label="์ƒํ•˜ ์—ฌ๋ฐฑ (px)",
info="์—ฌ๋ฐฑ-์นดํ”ผ-์—ฌ๋ฐฑ์˜ ์—ฌ๋ฐฑ ํฌ๊ธฐ",
interactive=True
)
with gr.Row():
text_gap = gr.Slider(
minimum=5, maximum=100, value=30, step=5,
label="๋ฉ”์ธโ†”์„œ๋ธŒ ๊ฐ„๊ฒฉ (px)",
info="๋ฉ”์ธ์นดํ”ผ์™€ ์„œ๋ธŒ์นดํ”ผ ์‚ฌ์ด ๊ฐ„๊ฒฉ",
interactive=True
)
# ํ˜„์žฌ ์ ์šฉ๋œ ์—ฌ๋ฐฑ ์ •๋ณด ํ‘œ์‹œ
margin_info = gr.Markdown(
"๐Ÿ’ก **ํ˜„์žฌ ์—ฌ๋ฐฑ ์ •๋ณด:** ์ด๋ฏธ์ง€ ์ƒ์„ฑ ํ›„ ์‹ค์ œ ์ ์šฉ๋œ ์ˆ˜์น˜๊ฐ€ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.",
visible=True
)
# ์ƒ์„ฑ๋œ ์ด๋ฏธ์ง€ ์„น์…˜
with gr.Column(elem_classes="custom-frame"):
gr.HTML('<div class="section-title"><img src="https://cdn-icons-png.flaticon.com/512/1375/1375106.png"> ์ƒ์„ฑ๋œ ์ด๋ฏธ์ง€</div>')
# ๐ŸŽฏ ํ•ต์‹ฌ: Gradio ๊ธฐ๋ณธ ์ด๋ฏธ์ง€ ์ถœ๋ ฅ (๋‹ค์šด๋กœ๋“œ ๋ฒ„ํŠผ ์ž๋™ ํฌํ•จ)
output_image = gr.Image(
label="์ƒ์„ฑ๋œ ์ด๋ฏธ์ง€",
show_download_button=True, # ๋‹ค์šด๋กœ๋“œ ๋ฒ„ํŠผ ํ‘œ์‹œ
show_share_button=False, # ๊ณต์œ  ๋ฒ„ํŠผ ์ˆจ๊น€
interactive=False,
type="pil", # PIL Image ํƒ€์ž…์œผ๋กœ ์„ค์ •
elem_classes="image-container output-image-container"
)
# ๐ŸŽฏ ํ•ต์‹ฌ: Gradio ๊ธฐ๋ณธ ํŒŒ์ผ ๋‹ค์šด๋กœ๋“œ ์ปดํฌ๋„ŒํŠธ
download_file = gr.File(
label="๐Ÿ“ฅ ์ด๋ฏธ์ง€ ๋‹ค์šด๋กœ๋“œ",
visible=True,
interactive=False
)
# ๐ŸŽฏ ํ•ต์‹ฌ: ์—”๋“œํฌ์ธํŠธ ๊ธฐ๋ฐ˜ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋“ค
def update_copy_type_description(selected_type):
"""์›๋ณธ๊ณผ ๋™์ผํ•œ ์นดํ”ผ ํƒ€์ž… ์„ ํƒ์‹œ ์„ค๋ช… ์—…๋ฐ์ดํŠธ"""
try:
client = get_client()
if not client:
return "โŒ ์„œ๋ฒ„ ์—ฐ๊ฒฐ ์‹คํŒจ"
result = client.predict(
selected_type=selected_type,
api_name="/update_copy_type_description"
)
return result
except Exception as e:
print(f"โŒ ์นดํ”ผ ํƒ€์ž… ์„ค๋ช… ์—…๋ฐ์ดํŠธ ์‹คํŒจ: {str(e)[:50]}...")
return f"โŒ ์˜ค๋ฅ˜: ์„ค๋ช… ์—…๋ฐ์ดํŠธ ์‹คํŒจ"
def handle_copy_generation(keyword, selected_type):
"""์›๋ณธ๊ณผ ๋™์ผํ•œ ์นดํ”ผ ์ƒ์„ฑ ์ฒ˜๋ฆฌ"""
try:
client = get_client()
if not client:
return ("โŒ ์„œ๋ฒ„ ์—ฐ๊ฒฐ ์‹คํŒจ", {}, "", "", "", "", "", "", "", keyword.strip() if keyword else "")
result = client.predict(
keyword=keyword,
selected_type=selected_type,
api_name="/handle_copy_generation"
)
# ์›๋ณธ๊ณผ ๋™์ผํ•œ ํŠœํ”Œ ๋ฐ˜ํ™˜ (10๊ฐœ ์š”์†Œ)
# [0] ์ƒํƒœ๋ฉ”์‹œ์ง€, [1] copy_suggestions_state, [2-6] ์นดํ”ผ1-5, [7-8] ๋ฉ”์ธ/์„œ๋ธŒ, [9] current_keyword_state
if len(result) >= 8:
# API์—์„œ 8๊ฐœ๋ฅผ ๋ฐ›์œผ๋ฉด ์ถ”๊ฐ€ ์ƒํƒœ๊ฐ’๋“ค์„ ๋กœ์ปฌ์—์„œ ์ฒ˜๋ฆฌ
return (result[0], {}, result[1], result[2], result[3], result[4], result[5], result[6], result[7], keyword.strip() if keyword else "")
else:
return result
except Exception as e:
print(f"โŒ ์นดํ”ผ ์ƒ์„ฑ ์‹คํŒจ: {str(e)[:50]}...")
error_msg = "โŒ ์นดํ”ผ ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค"
return (error_msg, {}, "", "", "", "", "", "", "", keyword.strip() if keyword else "")
def handle_copy_selection(copy_suggestions_state, selected_type, selected_copy):
"""์›๋ณธ๊ณผ ๋™์ผํ•œ ์นดํ”ผ ์„ ํƒ์‹œ ๋ฉ”์ธ/์„œ๋ธŒ ํ…์ŠคํŠธ๋ฐ•์Šค ์—…๋ฐ์ดํŠธ"""
try:
client = get_client()
if not client:
return ("", "")
result = client.predict(
selected_type=selected_type,
selected_copy=selected_copy,
api_name="/handle_copy_selection"
)
# ํŠœํ”Œ ๋ฐ˜ํ™˜ (2๊ฐœ ์š”์†Œ)
return result
except Exception as e:
print(f"โŒ ์นดํ”ผ ์„ ํƒ ์‹คํŒจ: {str(e)[:50]}...")
return ("", "")
def handle_image_generation(input_image, main_text, sub_text, color_mode,
main_font_choice, sub_font_choice, manual_bg_color, manual_main_text_color,
manual_sub_text_color, manual_main_font_size, manual_sub_font_size,
top_bottom_margin, text_gap, current_keyword):
"""์›๋ณธ๊ณผ ๋™์ผํ•œ ์ด๋ฏธ์ง€ ์ƒ์„ฑ ์ฒ˜๋ฆฌ (์—ฌ๋ฐฑ ์กฐ์ • ๊ธฐ๋Šฅ ํฌํ•จ)"""
try:
client = get_client()
if not client:
return (None, color_mode, manual_bg_color, manual_main_text_color,
manual_sub_text_color, manual_main_font_size, manual_sub_font_size,
None, "โŒ ์„œ๋ฒ„ ์—ฐ๊ฒฐ ์‹คํŒจ")
# ์ด๋ฏธ์ง€ ํŒŒ์ผ ํ•ธ๋“ค๋ง
image_file = None
if input_image:
image_file = handle_file(input_image)
result = client.predict(
input_image=image_file,
main_text=main_text,
sub_text=sub_text,
color_mode=color_mode,
main_font_choice=main_font_choice,
sub_font_choice=sub_font_choice,
manual_bg_color=manual_bg_color,
manual_main_text_color=manual_main_text_color,
manual_sub_text_color=manual_sub_text_color,
manual_main_font_size=manual_main_font_size,
manual_sub_font_size=manual_sub_font_size,
top_bottom_margin=top_bottom_margin,
text_gap=text_gap,
api_name="/handle_image_generation"
)
# ์›๋ณธ๊ณผ ๋™์ผํ•œ ํŠœํ”Œ ๋ฐ˜ํ™˜ (9๊ฐœ ์š”์†Œ)
# [0] ์ด๋ฏธ์ง€, [1] ์ƒ‰์ƒ๋ชจ๋“œ, [2] ๋ฐฐ๊ฒฝ์ƒ‰, [3] ๋ฉ”์ธํ…์ŠคํŠธ์ƒ‰, [4] ์„œ๋ธŒํ…์ŠคํŠธ์ƒ‰,
# [5] ๋ฉ”์ธํฐํŠธํฌ๊ธฐ, [6] ์„œ๋ธŒํฐํŠธํฌ๊ธฐ, [7] ๋‹ค์šด๋กœ๋“œํŒŒ์ผ, [8] ์—ฌ๋ฐฑ์ •๋ณด
return result
except Exception as e:
print(f"โŒ ์ด๋ฏธ์ง€ ์ƒ์„ฑ ์‹คํŒจ: {str(e)[:50]}...")
return (None, color_mode, manual_bg_color, manual_main_text_color,
manual_sub_text_color, manual_main_font_size, manual_sub_font_size,
None, "โŒ ์ด๋ฏธ์ง€ ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค")
# ์›๋ณธ๊ณผ ๋™์ผํ•œ ์ด๋ฒคํŠธ ์—ฐ๊ฒฐ
copy_type_selection.change(
fn=update_copy_type_description,
inputs=[copy_type_selection],
outputs=[copy_type_description]
)
generate_copy_btn.click(
fn=handle_copy_generation,
inputs=[product_keyword, copy_type_selection],
outputs=[copy_type_description, copy_suggestions_state,
copy1_display, copy2_display, copy3_display, copy4_display, copy5_display,
main_text, sub_text, current_keyword_state]
)
copy_selection.change(
fn=handle_copy_selection,
inputs=[copy_suggestions_state, copy_type_selection, copy_selection],
outputs=[main_text, sub_text]
)
generate_image_btn.click(
fn=handle_image_generation,
inputs=[input_image, main_text, sub_text, color_mode,
main_font_choice, sub_font_choice, manual_bg_color, manual_main_text_color, manual_sub_text_color,
manual_main_font_size, manual_sub_font_size, top_bottom_margin, text_gap, current_keyword_state],
outputs=[output_image, color_mode, manual_bg_color, manual_main_text_color, manual_sub_text_color,
manual_main_font_size, manual_sub_font_size, download_file, margin_info]
)
return app
if __name__ == "__main__":
app = main()
app.launch(share=False, inbrowser=True)