fh46jderf / app.py
ssboost's picture
Update app.py
7de8013 verified
raw
history blame
26.1 kB
import os
import sys
import logging
import tempfile
import traceback
from typing import List, Tuple, Optional, Any
import gradio as gr
from gradio_client import Client, handle_file
from dotenv import load_dotenv
import time
# .env ํŒŒ์ผ ๋กœ๋“œ
load_dotenv()
# ๋กœ๊น… ์„ค์ • (API ์—”๋“œํฌ์ธํŠธ ์ •๋ณด๋Š” ๋กœ๊ทธ์— ๋‚จ๊ธฐ์ง€ ์•Š์Œ)
class SafeFormatter(logging.Formatter):
"""API ์—”๋“œํฌ์ธํŠธ ์ •๋ณด๋ฅผ ๋กœ๊ทธ์—์„œ ์ œ๊ฑฐํ•˜๋Š” ์•ˆ์ „ํ•œ ํฌ๋งคํ„ฐ"""
def format(self, record):
# API_ENDPOINT ๊ด€๋ จ ์ •๋ณด ํ•„ํ„ฐ๋ง
msg = super().format(record)
# ๋ฏผ๊ฐํ•œ ์ •๋ณด๊ฐ€ ํฌํ•จ๋œ ๊ฒฝ์šฐ ๋งˆ์Šคํ‚น
if "API_ENDPOINT" in msg or "happydoggg" in msg or "49493h" in msg:
return msg.replace(os.environ.get("API_ENDPOINT", ""), "[API_ENDPOINT_HIDDEN]")
return msg
# ๋กœ๊ทธ ํ•ธ๋“ค๋Ÿฌ ์„ค์ •
file_handler = logging.FileHandler("app.log")
console_handler = logging.StreamHandler(sys.stdout)
formatter = SafeFormatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)
console_handler.setFormatter(formatter)
logger = logging.getLogger("image-enhancer-control-tower")
logger.setLevel(logging.INFO)
logger.addHandler(file_handler)
logger.addHandler(console_handler)
class GradioClientController:
"""ํ—ˆ๊น…ํŽ˜์ด์Šค ๊ทธ๋ผ๋””์˜ค API ํด๋ผ์ด์–ธํŠธ ์ปจํŠธ๋กค๋Ÿฌ"""
def __init__(self):
self.client = None
self.api_endpoint = None
self.background_options = {} # ์บ์‹œ๋œ ๋ฐฐ๊ฒฝ ์˜ต์…˜
self._initialize_client()
def _initialize_client(self):
"""ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐํ™” (์—”๋“œํฌ์ธํŠธ ์ •๋ณด๋Š” ๋กœ๊ทธ์— ๋‚จ๊ธฐ์ง€ ์•Š์Œ)"""
try:
self.api_endpoint = os.environ.get("API_ENDPOINT")
if not self.api_endpoint:
logger.error("API_ENDPOINT ํ™˜๊ฒฝ ๋ณ€์ˆ˜๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.")
raise ValueError("API_ENDPOINT ํ™˜๊ฒฝ ๋ณ€์ˆ˜๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.")
# ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐํ™” ์‹œ๋„ (์žฌ์‹œ๋„ ๋กœ์ง ํฌํ•จ)
max_retries = 3
for attempt in range(max_retries):
try:
self.client = Client(self.api_endpoint)
logger.info("API ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์ดˆ๊ธฐํ™”๋˜์—ˆ์Šต๋‹ˆ๋‹ค.")
# ์ดˆ๊ธฐํ™” ์„ฑ๊ณต ์‹œ ์‹ฌํ”Œ ๋ฐฐ๊ฒฝ ์˜ต์…˜ ๋จผ์ € ๋กœ๋“œ
self._load_simple_background()
return
except Exception as e:
if attempt < max_retries - 1:
logger.warning(f"ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐํ™” ์žฌ์‹œ๋„ {attempt + 1}/{max_retries}")
time.sleep(2)
else:
raise e
except Exception as e:
logger.error(f"ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐํ™” ์‹คํŒจ: {str(e)}")
self.client = None
def _load_simple_background(self):
"""์‹ฌํ”Œ ๋ฐฐ๊ฒฝ ์˜ต์…˜๋งŒ ๋จผ์ € ๋กœ๋“œ"""
try:
if self.client:
result = self.client.predict("์‹ฌํ”Œ ๋ฐฐ๊ฒฝ", api_name="/update_dropdowns")
if isinstance(result, (list, tuple)) and len(result) >= 7:
self.background_options["์‹ฌํ”Œ ๋ฐฐ๊ฒฝ"] = result
logger.info("์‹ฌํ”Œ ๋ฐฐ๊ฒฝ ์˜ต์…˜ ๋กœ๋“œ ์™„๋ฃŒ")
return result[0] # ์‹ฌํ”Œ ๋ฐฐ๊ฒฝ ์„ ํƒ์ง€ ๋ฐ˜ํ™˜
except Exception as e:
logger.warning(f"์‹ฌํ”Œ ๋ฐฐ๊ฒฝ ์˜ต์…˜ ๋กœ๋“œ ์‹คํŒจ: {str(e)}")
return []
def get_initial_dropdown_data(self, bg_type: str = "์‹ฌํ”Œ ๋ฐฐ๊ฒฝ") -> Tuple:
"""์ดˆ๊ธฐ ๋“œ๋กญ๋‹ค์šด ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ"""
try:
# ์บ์‹œ๋œ ๋ฐ์ดํ„ฐ ๋จผ์ € ํ™•์ธ
if bg_type in self.background_options:
result = self.background_options[bg_type]
logger.info(f"์บ์‹œ์—์„œ ์ดˆ๊ธฐ ๋“œ๋กญ๋‹ค์šด ๋ฐ์ดํ„ฐ ๋กœ๋“œ: {bg_type}")
else:
# API ํ˜ธ์ถœ๋กœ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ
if self.client:
result = self.client.predict(bg_type, api_name="/update_dropdowns")
self.background_options[bg_type] = result # ์บ์‹œ์— ์ €์žฅ
logger.info(f"API์—์„œ ์ดˆ๊ธฐ ๋“œ๋กญ๋‹ค์šด ๋ฐ์ดํ„ฐ ๋กœ๋“œ: {bg_type}")
else:
logger.error("ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ดˆ๊ธฐํ™”๋˜์ง€ ์•Š์Œ")
return tuple([[] for _ in range(7)])
# ๊ฒฐ๊ณผ ๋ฐ˜ํ™˜ (์‹ค์ œ ์„ ํƒ์ง€๋งŒ)
if isinstance(result, (list, tuple)) and len(result) >= 7:
return result[:7]
else:
return tuple([[] for _ in range(7)])
except Exception as e:
logger.error(f"์ดˆ๊ธฐ ๋“œ๋กญ๋‹ค์šด ๋ฐ์ดํ„ฐ ๋กœ๋“œ ์‹คํŒจ: {str(e)}")
return tuple([[] for _ in range(7)])
def _parse_dropdown_result(self, result):
"""API ์‘๋‹ต์—์„œ ์‹ค์ œ ์„ ํƒ์ง€ ๋ฐ์ดํ„ฐ๋งŒ ์ถ”์ถœ - ๋‹จ์ˆœํ™”"""
try:
logger.info(f"์‘๋‹ต ํŒŒ์‹ฑ: {result}")
# API ๋ฌธ์„œ์— ๋”ฐ๋ฅด๋ฉด tuple of 7 elements๋ฅผ ๋ฐ˜ํ™˜
# ๊ฐ ์š”์†Œ๋Š” Literal ํƒ€์ž…์˜ ๋ฆฌ์ŠคํŠธ์—ฌ์•ผ ํ•จ
if isinstance(result, (list, tuple)) and len(result) >= 7:
# ๊ฒฐ๊ณผ๋ฅผ ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜ (API๊ฐ€ ์ด๋ฏธ ์˜ฌ๋ฐ”๋ฅธ ํ˜•์‹์œผ๋กœ ๋ฐ˜ํ™˜ํ•œ๋‹ค๊ณ  ๊ฐ€์ •)
return result[:7]
else:
logger.warning(f"์˜ˆ์ƒํ•˜์ง€ ๋ชปํ•œ ์‘๋‹ต ํ˜•์‹: {type(result)}, length={len(result) if hasattr(result, '__len__') else 'N/A'}")
return [[] for _ in range(7)]
except Exception as e:
logger.error(f"๋“œ๋กญ๋‹ค์šด ๊ฒฐ๊ณผ ํŒŒ์‹ฑ ์˜ค๋ฅ˜: {str(e)}")
return [[] for _ in range(7)]
def get_initial_simple_choices(self) -> list:
"""์‹ฌํ”Œ ๋ฐฐ๊ฒฝ ์ดˆ๊ธฐ ์„ ํƒ์ง€ - ํ•˜๋“œ์ฝ”๋”ฉ์œผ๋กœ ํ…Œ์ŠคํŠธ"""
# API ๋ฌธ์„œ์—์„œ ํ™•์ธ๋œ ์‹ค์ œ ์„ ํƒ์ง€๋“ค
return [
'ํ™”์ดํŠธ ๊ธฐ๋ณธ', 'ํšŒ์ƒ‰ ํˆฌํ†ค', '๋ผ์ดํŠธ ๊ทธ๋ ˆ์ด', '๊ทธ๋ ˆ์ด ๊ทธ๋ผ๋ฐ์ด์…˜ ์ŠคํฌํŠธ๋ผ์ดํŠธ',
'ํ”„๋ฆฌ๋ฏธ์—„ ๋“œ๋ผ๋งˆํ‹ฑ ๋ธ”๋ž™', '๋”ฅ๋ธ”๋ฃจ ์œ ๋ฆฌ๋ฐ˜์‚ฌ', 'ํŒŒ์Šคํ…” ๊ทธ๋ผ๋ฐ์ด์…˜', '์Šค์นด์ด๋ธ”๋ฃจ ํŒŒ์Šคํ…”',
'๋ฒ„ํ„ฐ์˜๋กœ์šฐ ํŒŒ์Šคํ…”', '๋ธ”๋ฃจ ์›์ƒ‰', '๋ ˆ๋“œ ์›์ƒ‰'
]
def _ensure_client(self) -> bool:
"""ํด๋ผ์ด์–ธํŠธ ์—ฐ๊ฒฐ ์ƒํƒœ ํ™•์ธ ๋ฐ ์žฌ์—ฐ๊ฒฐ"""
if self.client is None:
logger.info("ํด๋ผ์ด์–ธํŠธ ์žฌ์—ฐ๊ฒฐ ์‹œ๋„")
self._initialize_client()
return self.client is not None
def update_dropdowns(self, bg_type: str) -> Tuple:
"""๋ฐฐ๊ฒฝ ์œ ํ˜•์— ๋”ฐ๋ฅธ ๋“œ๋กญ๋‹ค์šด ์—…๋ฐ์ดํŠธ - ๋‹จ์ˆœํ™”"""
try:
logger.info(f"๋“œ๋กญ๋‹ค์šด ์—…๋ฐ์ดํŠธ: {bg_type}")
if not self._ensure_client():
logger.error("ํด๋ผ์ด์–ธํŠธ ์—ฐ๊ฒฐ ์‹คํŒจ")
return tuple([gr.update(visible=False) for _ in range(7)])
# API ํ˜ธ์ถœ
result = self.client.predict(bg_type, api_name="/update_dropdowns")
logger.info(f"API ์‘๋‹ต: {result}")
# API ์‘๋‹ต์„ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉ
if isinstance(result, (list, tuple)) and len(result) >= 7:
updates = []
visibility_map = ["์‹ฌํ”Œ ๋ฐฐ๊ฒฝ", "์ŠคํŠœ๋””์˜ค ๋ฐฐ๊ฒฝ", "์ž์—ฐ ํ™˜๊ฒฝ", "์‹ค๋‚ด ํ™˜๊ฒฝ", "ํŠน์ˆ˜๋ฐฐ๊ฒฝ", "์ฃผ์–ผ๋ฆฌ", "ํŠน์ˆ˜ํšจ๊ณผ"]
for i, choices in enumerate(result[:7]):
is_visible = (bg_type == visibility_map[i])
if is_visible and isinstance(choices, list) and len(choices) > 0:
logger.info(f"{visibility_map[i]} ๋“œ๋กญ๋‹ค์šด: {len(choices)}๊ฐœ ์„ ํƒ์ง€")
updates.append(gr.update(
visible=True,
choices=choices,
value=choices[0], # ์ฒซ ๋ฒˆ์งธ ํ•ญ๋ชฉ ์„ ํƒ
interactive=True
))
else:
updates.append(gr.update(visible=False))
return tuple(updates)
else:
logger.error(f"์ž˜๋ชป๋œ API ์‘๋‹ต: {result}")
return tuple([gr.update(visible=False) for _ in range(7)])
except Exception as e:
logger.error(f"๋“œ๋กญ๋‹ค์šด ์—…๋ฐ์ดํŠธ ์˜ค๋ฅ˜: {str(e)}")
return tuple([gr.update(visible=False) for _ in range(7)])
def generate_prompt_only(self, password: str, bg_type: str, simple: str, studio: str,
nature: str, indoor: str, special: str, jewelry: str,
special_effects: str, request_text: str, aspect_ratio: str) -> str:
"""ํ”„๋กฌํ”„ํŠธ๋งŒ ์ƒ์„ฑ"""
try:
if not self._ensure_client():
return "ํด๋ผ์ด์–ธํŠธ ์—ฐ๊ฒฐ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. ์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”."
result = self.client.predict(
password,
bg_type,
simple or "",
studio or "",
nature or "",
indoor or "",
special or "",
jewelry or "",
special_effects or "",
request_text or "",
aspect_ratio,
api_name="/generate_prompt_with_password_check"
)
logger.info("ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ ์™„๋ฃŒ")
return result if result else "ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค."
except Exception as e:
logger.error(f"ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ ์˜ค๋ฅ˜: {str(e)}")
return f"ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ ์˜ค๋ฅ˜: {str(e)}"
def process_image(self, password: str, image, bg_type: str, simple: str, studio: str,
nature: str, indoor: str, special: str, jewelry: str, special_effects: str,
request_text: str, quality_level: str, aspect_ratio: str,
output_format: str, enable_enhancement: bool) -> Tuple:
"""์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ (ํŽธ์ง‘ ๋ฐ ํ™”์งˆ ๊ฐœ์„ ) - API ๋ฌธ์„œ์™€ ์ •ํ™•ํžˆ ๋งค์นญ"""
temp_path = None
try:
if not self._ensure_client():
return ([], None, [], None, "", "", "ํด๋ผ์ด์–ธํŠธ ์—ฐ๊ฒฐ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. ์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”.")
# ์ด๋ฏธ์ง€ ๊ฒ€์ฆ
if image is None:
return ([], None, [], None, "", "", "์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•ด์ฃผ์„ธ์š”.")
# ์ด๋ฏธ์ง€๋ฅผ ์ž„์‹œ ํŒŒ์ผ๋กœ ์ €์žฅ
temp_path = tempfile.mktemp(suffix='.png')
try:
image.save(temp_path, format='PNG')
logger.info(f"์ด๋ฏธ์ง€๋ฅผ ์ž„์‹œ ํŒŒ์ผ๋กœ ์ €์žฅ: {os.path.basename(temp_path)}")
except Exception as e:
logger.error(f"์ด๋ฏธ์ง€ ์ €์žฅ ์‹คํŒจ: {str(e)}")
return ([], None, [], None, "", "", "์ด๋ฏธ์ง€ ์ €์žฅ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ์ด๋ฏธ์ง€๋ฅผ ์‹œ๋„ํ•ด๋ณด์„ธ์š”.")
# API ํ˜ธ์ถœ - ๋งค๊ฐœ๋ณ€์ˆ˜ ์ˆœ์„œ๋ฅผ API ๋ฌธ์„œ์™€ ์ •ํ™•ํžˆ ๋งž์ถค
logger.info("์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ API ํ˜ธ์ถœ ์‹œ์ž‘")
result = self.client.predict(
password, # password (str)
handle_file(temp_path), # param_1 (image file)
bg_type, # param_2 (๋ฐฐ๊ฒฝ ์œ ํ˜•)
simple or "", # param_3 (์‹ฌํ”Œ ๋ฐฐ๊ฒฝ)
studio or "", # param_4 (์ŠคํŠœ๋””์˜ค ๋ฐฐ๊ฒฝ)
nature or "", # param_5 (์ž์—ฐ ํ™˜๊ฒฝ)
indoor or "", # param_6 (์‹ค๋‚ด ํ™˜๊ฒฝ)
special or "", # param_7 (ํŠน์ˆ˜๋ฐฐ๊ฒฝ)
jewelry or "", # param_8 (์ฃผ์–ผ๋ฆฌ)
special_effects or "", # param_9 (ํŠน์ˆ˜ํšจ๊ณผ)
request_text or "", # param_10 (์š”์ฒญ์‚ฌํ•ญ)
quality_level, # param_11 (ํ’ˆ์งˆ ๋ ˆ๋ฒจ)
aspect_ratio, # param_12 (์ข…ํšก๋น„)
output_format, # param_13 (์ด๋ฏธ์ง€ ํ˜•์‹)
enable_enhancement, # param_14 (์ถ”๊ฐ€ ํ™”์งˆ ๊ฐœ์„ )
api_name="/check_password"
)
logger.info("์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ API ํ˜ธ์ถœ ์™„๋ฃŒ")
# ๊ฒฐ๊ณผ ๊ฒ€์ฆ ๋ฐ ๋ฐ˜ํ™˜ - API ๋ฌธ์„œ์— ๋”ฐ๋ฅด๋ฉด tuple of 7 elements
if isinstance(result, (list, tuple)) and len(result) >= 7:
# [0] original_gallery, [1] original_file, [2] enhanced_gallery,
# [3] enhanced_file, [4] prompt_text, [5] info_text, [6] error_text
return result[:7] # ์ •ํ™•ํžˆ 7๊ฐœ ์š”์†Œ๋งŒ ๋ฐ˜ํ™˜
else:
logger.warning(f"API ์‘๋‹ต ํ˜•์‹ ์ด์ƒ: type={type(result)}, length={len(result) if hasattr(result, '__len__') else 'N/A'}")
return ([], None, [], None, "", "", "API์—์„œ ์˜ฌ๋ฐ”๋ฅด์ง€ ์•Š์€ ์‘๋‹ต์„ ๋ฐ›์•˜์Šต๋‹ˆ๋‹ค.")
except Exception as e:
error_msg = str(e)
logger.error(f"์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ ์˜ค๋ฅ˜: {error_msg}")
# ์ผ๋ฐ˜์ ์ธ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€ ๊ฐœ์„ 
if "timeout" in error_msg.lower():
user_error = "์š”์ฒญ ์ฒ˜๋ฆฌ ์‹œ๊ฐ„์ด ์ดˆ๊ณผ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”."
elif "connection" in error_msg.lower():
user_error = "์„œ๋ฒ„ ์—ฐ๊ฒฐ์— ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๋„คํŠธ์›Œํฌ ์ƒํƒœ๋ฅผ ํ™•์ธํ•ด์ฃผ์„ธ์š”."
elif "unauthorized" in error_msg.lower() or "password" in error_msg.lower():
user_error = "์ธ์ฆ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค. ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ํ™•์ธํ•ด์ฃผ์„ธ์š”."
else:
user_error = f"์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค: {error_msg}"
return ([], None, [], None, "", "", user_error)
finally:
# ์ž„์‹œ ํŒŒ์ผ ์ •๋ฆฌ
if temp_path and os.path.exists(temp_path):
try:
os.remove(temp_path)
logger.info("์ž„์‹œ ํŒŒ์ผ ์ •๋ฆฌ ์™„๋ฃŒ")
except Exception as e:
logger.warning(f"์ž„์‹œ ํŒŒ์ผ ์ •๋ฆฌ ์‹คํŒจ: {str(e)}")
# ์ „์—ญ ํด๋ผ์ด์–ธํŠธ ์ปจํŠธ๋กค๋Ÿฌ ์ธ์Šคํ„ด์Šค
controller = GradioClientController()
def create_gradio_interface():
"""Gradio ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ"""
try:
logger.info("Gradio ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ ์‹œ์ž‘")
# ๋ฐฐ๊ฒฝ ์œ ํ˜• ์„ ํƒ์ง€
background_types = ["์‹ฌํ”Œ ๋ฐฐ๊ฒฝ", "์ŠคํŠœ๋””์˜ค ๋ฐฐ๊ฒฝ", "์ž์—ฐ ํ™˜๊ฒฝ", "์‹ค๋‚ด ํ™˜๊ฒฝ", "ํŠน์ˆ˜๋ฐฐ๊ฒฝ", "์ฃผ์–ผ๋ฆฌ", "ํŠน์ˆ˜ํšจ๊ณผ"]
# ์ปค์Šคํ…€ CSS ์ถ”๊ฐ€ (์›๋ณธ๊ณผ ๋™์ผํ•œ ์Šคํƒ€์ผ ์œ ์ง€)
css = """
.gradio-container {
max-width: 1200px !important;
}
"""
with gr.Blocks(title="AI ์ด๋ฏธ์ง€ ํŽธ์ง‘ ๋ฐ ํ™”์งˆ ๊ฐœ์„ ", css=css, theme=gr.themes.Soft()) as app:
gr.Markdown("# AI ์ด๋ฏธ์ง€ ํŽธ์ง‘ ๋ฐ ํ™”์งˆ ๊ฐœ์„  ๋„๊ตฌ")
# ๋น„๋ฐ€๋ฒˆํ˜ธ ์ž…๋ ฅ ํ•„๋“œ
password_box = gr.Textbox(
label="๋น„๋ฐ€๋ฒˆํ˜ธ",
type="password",
placeholder="์‚ฌ์šฉํ•˜๋ ค๋ฉด ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”",
interactive=True
)
# ์ด๋ฏธ์ง€ ํŽธ์ง‘ ๋ฐ ํ™”์งˆ ๊ฐœ์„  ์ธํ„ฐํŽ˜์ด์Šค
with gr.Row():
with gr.Column():
# ์ƒํ’ˆ ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ
image = gr.Image(label="์ƒํ’ˆ ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ", type="pil")
with gr.Row():
with gr.Column():
background_type = gr.Radio(
choices=background_types,
label="๋ฐฐ๊ฒฝ ์œ ํ˜•",
value="์‹ฌํ”Œ ๋ฐฐ๊ฒฝ"
)
# ๋“œ๋กญ๋‹ค์šด ์ปดํฌ๋„ŒํŠธ๋“ค - ์‹ฌํ”Œ ๋ฐฐ๊ฒฝ์€ ํ•˜๋“œ์ฝ”๋”ฉ๋œ ์„ ํƒ์ง€๋กœ ์‹œ์ž‘
simple_dropdown = gr.Dropdown(
choices=controller.get_initial_simple_choices(),
value="ํ™”์ดํŠธ ๊ธฐ๋ณธ", # ๊ธฐ๋ณธ๊ฐ’ ์„ค์ •
label="์‹ฌํ”Œ ๋ฐฐ๊ฒฝ ์„ ํƒ",
visible=True,
interactive=True
)
studio_dropdown = gr.Dropdown(
choices=[],
label="์ŠคํŠœ๋””์˜ค ๋ฐฐ๊ฒฝ ์„ ํƒ",
visible=False,
interactive=True
)
nature_dropdown = gr.Dropdown(
choices=[],
label="์ž์—ฐ ํ™˜๊ฒฝ ์„ ํƒ",
visible=False,
interactive=True
)
indoor_dropdown = gr.Dropdown(
choices=[],
label="์‹ค๋‚ด ํ™˜๊ฒฝ ์„ ํƒ",
visible=False,
interactive=True
)
special_dropdown = gr.Dropdown(
choices=[],
label="ํŠน์ˆ˜๋ฐฐ๊ฒฝ ์„ ํƒ",
visible=False,
interactive=True
)
jewelry_dropdown = gr.Dropdown(
choices=[],
label="์ฃผ์–ผ๋ฆฌ ๋ฐฐ๊ฒฝ ์„ ํƒ",
visible=False,
interactive=True
)
special_effects_dropdown = gr.Dropdown(
choices=[],
label="ํŠน์ˆ˜ํšจ๊ณผ ๋ฐฐ๊ฒฝ ์„ ํƒ",
visible=False,
interactive=True
)
# ์š”์ฒญ์‚ฌํ•ญ ์ž…๋ ฅ
request_text = gr.Textbox(
label="์š”์ฒญ์‚ฌํ•ญ",
placeholder="์ƒํ’ˆ ์ด๋ฏธ์ง€์— ์ ์šฉํ•  ์Šคํƒ€์ผ, ๋ถ„์œ„๊ธฐ, ํŠน๋ณ„ ์š”์ฒญ์‚ฌํ•ญ ๋“ฑ์„ ์ž…๋ ฅํ•˜์„ธ์š”.",
lines=3
)
# ์ƒˆ๋กœ์šด ์˜ต์…˜๋“ค
quality_level = gr.Radio(
label="ํ’ˆ์งˆ ๋ ˆ๋ฒจ",
choices=["gpt", "flux"],
value="flux",
info="GPT: GPT ๋ชจ๋ธ (๊ณ ํ’ˆ์งˆ), Flux: Flux ๋ชจ๋ธ (๋น ๋ฅธ ์ฒ˜๋ฆฌ + ๊ธฐ๋ณธ ํ™”์งˆ๊ฐœ์„ )"
)
aspect_ratio = gr.Dropdown(
label="์ข…ํšก๋น„",
choices=["1:1", "3:2", "2:3"],
value="1:1"
)
output_format = gr.Dropdown(
label="์ด๋ฏธ์ง€ ํ˜•์‹",
choices=["jpg", "png"],
value="jpg"
)
# ํ™”์งˆ ๊ฐœ์„  ์˜ต์…˜
enable_enhancement = gr.Checkbox(
label="์ถ”๊ฐ€ ํ™”์งˆ ๊ฐœ์„ ",
value=False,
info="GPT: 1ํšŒ ํ™”์งˆ๊ฐœ์„ , Flux: 2์ฐจ ํ™”์งˆ๊ฐœ์„  (๊ธฐ๋ณธ 1ํšŒ + ์ถ”๊ฐ€ 1ํšŒ)"
)
# ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ ๋ฒ„ํŠผ
generate_prompt_btn = gr.Button("ํ”„๋กฌํ”„ํŠธ๋งŒ ์ƒ์„ฑ", variant="secondary")
# ํŽธ์ง‘ ๋ฒ„ํŠผ
edit_btn = gr.Button("์ด๋ฏธ์ง€ ํŽธ์ง‘ ๋ฐ ํ™”์งˆ ๊ฐœ์„ ", variant="primary")
with gr.Column():
with gr.Row():
with gr.Column():
gr.Markdown("## ํŽธ์ง‘๋œ ์ด๋ฏธ์ง€")
original_output = gr.Gallery(label="ํŽธ์ง‘ ๊ฒฐ๊ณผ", preview=True)
original_download = gr.File(label="ํŽธ์ง‘ ์ด๋ฏธ์ง€ ๋‹ค์šด๋กœ๋“œ", interactive=False)
with gr.Column():
gr.Markdown("## ํ™”์งˆ ๊ฐœ์„ ๋œ ์ด๋ฏธ์ง€")
enhanced_output = gr.Gallery(label="ํ™”์งˆ ๊ฐœ์„  ๊ฒฐ๊ณผ", preview=True)
enhanced_download = gr.File(label="๊ฐœ์„  ์ด๋ฏธ์ง€ ๋‹ค์šด๋กœ๋“œ", interactive=False)
# ํ”„๋กฌํ”„ํŠธ ์ถœ๋ ฅ
prompt_output = gr.Textbox(label="์ƒ์„ฑ๋œ ํ”„๋กฌํ”„ํŠธ", lines=10, interactive=False)
info = gr.Textbox(label="์ฒ˜๋ฆฌ ์ •๋ณด", interactive=False)
error = gr.Textbox(label="์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€", interactive=False, visible=True)
# ๋“œ๋กญ๋‹ค์šด ๋ณ€๊ฒฝ ์ด๋ฒคํŠธ - ์ฆ‰์‹œ ์—…๋ฐ์ดํŠธ
background_type.change(
fn=controller.update_dropdowns,
inputs=[background_type],
outputs=[simple_dropdown, studio_dropdown, nature_dropdown,
indoor_dropdown, special_dropdown, jewelry_dropdown, special_effects_dropdown],
queue=False # ํ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•„ ๋” ๋น ๋ฅธ ์‘๋‹ต
)
# ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ ๋ฒ„ํŠผ ํด๋ฆญ ์ด๋ฒคํŠธ
generate_prompt_btn.click(
fn=controller.generate_prompt_only,
inputs=[
password_box,
background_type,
simple_dropdown, studio_dropdown, nature_dropdown, indoor_dropdown, special_dropdown,
jewelry_dropdown, special_effects_dropdown,
request_text, aspect_ratio
],
outputs=[prompt_output]
)
# ํŽธ์ง‘ ๋ฒ„ํŠผ ํด๋ฆญ ์ด๋ฒคํŠธ
edit_btn.click(
fn=controller.process_image,
inputs=[
password_box,
image, background_type,
simple_dropdown, studio_dropdown, nature_dropdown, indoor_dropdown, special_dropdown,
jewelry_dropdown, special_effects_dropdown,
request_text, quality_level, aspect_ratio, output_format, enable_enhancement
],
outputs=[
original_output, original_download,
enhanced_output, enhanced_download,
prompt_output, info, error
]
)
# ์•ฑ ๋กœ๋“œ ์‹œ ๋ชจ๋“  ๋“œ๋กญ๋‹ค์šด ์ดˆ๊ธฐ ์„ค์ • (๋” ๊ฐ•๋ ฅํ•œ ์ดˆ๊ธฐํ™”)
def initialize_all_dropdowns():
"""๋ชจ๋“  ๋“œ๋กญ๋‹ค์šด์„ ์ดˆ๊ธฐํ™”ํ•˜๋Š” ํ•จ์ˆ˜"""
try:
logger.info("๋“œ๋กญ๋‹ค์šด ์ดˆ๊ธฐํ™” ์‹œ์ž‘")
updates = controller.update_dropdowns("์‹ฌํ”Œ ๋ฐฐ๊ฒฝ")
logger.info("๋“œ๋กญ๋‹ค์šด ์ดˆ๊ธฐํ™” ์™„๋ฃŒ")
return updates
except Exception as e:
logger.error(f"๋“œ๋กญ๋‹ค์šด ์ดˆ๊ธฐํ™” ์‹คํŒจ: {str(e)}")
return tuple([gr.update() for _ in range(7)])
app.load(
fn=initialize_all_dropdowns,
outputs=[simple_dropdown, studio_dropdown, nature_dropdown,
indoor_dropdown, special_dropdown, jewelry_dropdown, special_effects_dropdown]
)
logger.info("Gradio ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ ์™„๋ฃŒ")
return app
except Exception as e:
logger.error(f"Gradio ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ ์˜ค๋ฅ˜: {str(e)}")
logger.error(traceback.format_exc())
raise
# ์•ฑ ์‹คํ–‰
if __name__ == "__main__":
try:
logger.info("์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹œ์ž‘")
# imgs ๋””๋ ‰ํ† ๋ฆฌ ํ™•์ธ/์ƒ์„ฑ
os.makedirs("imgs", exist_ok=True)
logger.info("์ด๋ฏธ์ง€ ๋””๋ ‰ํ† ๋ฆฌ ์ค€๋น„ ์™„๋ฃŒ")
# API ์—”๋“œํฌ์ธํŠธ ํ™•์ธ
if not os.environ.get("API_ENDPOINT"):
logger.error("API_ENDPOINT ํ™˜๊ฒฝ ๋ณ€์ˆ˜๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.")
print("\nโŒ ์˜ค๋ฅ˜: API_ENDPOINT ํ™˜๊ฒฝ ๋ณ€์ˆ˜๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.")
print("ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ์„ค์ •ํ•˜๊ฑฐ๋‚˜ .env ํŒŒ์ผ์— API_ENDPOINT๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ์„ธ์š”.")
sys.exit(1)
app = create_gradio_interface()
logger.info("Gradio ์•ฑ ์‹œ์ž‘")
app.launch(
share=True,
server_name="0.0.0.0",
server_port=7860,
show_error=True,
quiet=False
)
except KeyboardInterrupt:
logger.info("์‚ฌ์šฉ์ž์— ์˜ํ•ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์ค‘๋‹จ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.")
except Exception as e:
logger.error(f"์•ฑ ์‹คํ–‰ ์˜ค๋ฅ˜: {str(e)}")
logger.error(traceback.format_exc())
print(f"\nโŒ ์‹ฌ๊ฐํ•œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค: {str(e)}")
print("์ž์„ธํ•œ ๋‚ด์šฉ์€ app.log ํŒŒ์ผ์„ ํ™•์ธํ•ด์ฃผ์„ธ์š”.")