ssboost commited on
Commit
f33cb93
ยท
verified ยท
1 Parent(s): 7de8013

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +273 -363
app.py CHANGED
@@ -1,320 +1,147 @@
1
  import os
2
  import sys
 
 
3
  import logging
4
  import tempfile
5
  import traceback
6
- from typing import List, Tuple, Optional, Any
 
7
  import gradio as gr
8
- from gradio_client import Client, handle_file
9
  from dotenv import load_dotenv
10
- import time
11
 
12
- # .env ํŒŒ์ผ ๋กœ๋“œ
13
  load_dotenv()
14
 
15
- # ๋กœ๊น… ์„ค์ • (API ์—”๋“œํฌ์ธํŠธ ์ •๋ณด๋Š” ๋กœ๊ทธ์— ๋‚จ๊ธฐ์ง€ ์•Š์Œ)
16
- class SafeFormatter(logging.Formatter):
17
- """API ์—”๋“œํฌ์ธํŠธ ์ •๋ณด๋ฅผ ๋กœ๊ทธ์—์„œ ์ œ๊ฑฐํ•˜๋Š” ์•ˆ์ „ํ•œ ํฌ๋งคํ„ฐ"""
18
- def format(self, record):
19
- # API_ENDPOINT ๊ด€๋ จ ์ •๋ณด ํ•„ํ„ฐ๋ง
20
- msg = super().format(record)
21
- # ๋ฏผ๊ฐํ•œ ์ •๋ณด๊ฐ€ ํฌํ•จ๋œ ๊ฒฝ์šฐ ๋งˆ์Šคํ‚น
22
- if "API_ENDPOINT" in msg or "happydoggg" in msg or "49493h" in msg:
23
- return msg.replace(os.environ.get("API_ENDPOINT", ""), "[API_ENDPOINT_HIDDEN]")
24
- return msg
25
 
26
- # ๋กœ๊ทธ ํ•ธ๋“ค๋Ÿฌ ์„ค์ •
27
- file_handler = logging.FileHandler("app.log")
28
- console_handler = logging.StreamHandler(sys.stdout)
29
- formatter = SafeFormatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
30
- file_handler.setFormatter(formatter)
31
- console_handler.setFormatter(formatter)
32
 
33
- logger = logging.getLogger("image-enhancer-control-tower")
34
- logger.setLevel(logging.INFO)
35
- logger.addHandler(file_handler)
36
- logger.addHandler(console_handler)
 
 
 
37
 
38
- class GradioClientController:
39
- """ํ—ˆ๊น…ํŽ˜์ด์Šค ๊ทธ๋ผ๋””์˜ค API ํด๋ผ์ด์–ธํŠธ ์ปจํŠธ๋กค๋Ÿฌ"""
40
-
41
- def __init__(self):
42
- self.client = None
43
- self.api_endpoint = None
44
- self.background_options = {} # ์บ์‹œ๋œ ๋ฐฐ๊ฒฝ ์˜ต์…˜
45
- self._initialize_client()
46
 
47
- def _initialize_client(self):
48
- """ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐํ™” (์—”๋“œํฌ์ธํŠธ ์ •๋ณด๋Š” ๋กœ๊ทธ์— ๋‚จ๊ธฐ์ง€ ์•Š์Œ)"""
49
- try:
50
- self.api_endpoint = os.environ.get("API_ENDPOINT")
51
- if not self.api_endpoint:
52
- logger.error("API_ENDPOINT ํ™˜๊ฒฝ ๋ณ€์ˆ˜๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.")
53
- raise ValueError("API_ENDPOINT ํ™˜๊ฒฝ ๋ณ€์ˆ˜๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.")
54
-
55
- # ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐํ™” ์‹œ๋„ (์žฌ์‹œ๋„ ๋กœ์ง ํฌํ•จ)
56
- max_retries = 3
57
- for attempt in range(max_retries):
58
- try:
59
- self.client = Client(self.api_endpoint)
60
- logger.info("API ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์ดˆ๊ธฐํ™”๋˜์—ˆ์Šต๋‹ˆ๋‹ค.")
61
- # ์ดˆ๊ธฐํ™” ์„ฑ๊ณต ์‹œ ์‹ฌํ”Œ ๋ฐฐ๊ฒฝ ์˜ต์…˜ ๋จผ์ € ๋กœ๋“œ
62
- self._load_simple_background()
63
- return
64
- except Exception as e:
65
- if attempt < max_retries - 1:
66
- logger.warning(f"ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐํ™” ์žฌ์‹œ๋„ {attempt + 1}/{max_retries}")
67
- time.sleep(2)
68
- else:
69
- raise e
70
-
71
- except Exception as e:
72
- logger.error(f"ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐํ™” ์‹คํŒจ: {str(e)}")
73
- self.client = None
74
-
75
- def _load_simple_background(self):
76
- """์‹ฌํ”Œ ๋ฐฐ๊ฒฝ ์˜ต์…˜๋งŒ ๋จผ์ € ๋กœ๋“œ"""
77
- try:
78
- if self.client:
79
- result = self.client.predict("์‹ฌํ”Œ ๋ฐฐ๊ฒฝ", api_name="/update_dropdowns")
80
- if isinstance(result, (list, tuple)) and len(result) >= 7:
81
- self.background_options["์‹ฌํ”Œ ๋ฐฐ๊ฒฝ"] = result
82
- logger.info("์‹ฌํ”Œ ๋ฐฐ๊ฒฝ ์˜ต์…˜ ๋กœ๋“œ ์™„๋ฃŒ")
83
- return result[0] # ์‹ฌํ”Œ ๋ฐฐ๊ฒฝ ์„ ํƒ์ง€ ๋ฐ˜ํ™˜
84
- except Exception as e:
85
- logger.warning(f"์‹ฌํ”Œ ๋ฐฐ๊ฒฝ ์˜ต์…˜ ๋กœ๋“œ ์‹คํŒจ: {str(e)}")
86
- return []
87
- def get_initial_dropdown_data(self, bg_type: str = "์‹ฌํ”Œ ๋ฐฐ๊ฒฝ") -> Tuple:
88
- """์ดˆ๊ธฐ ๋“œ๋กญ๋‹ค์šด ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ"""
89
- try:
90
- # ์บ์‹œ๋œ ๋ฐ์ดํ„ฐ ๋จผ์ € ๏ฟฝ๏ฟฝ์ธ
91
- if bg_type in self.background_options:
92
- result = self.background_options[bg_type]
93
- logger.info(f"์บ์‹œ์—์„œ ์ดˆ๊ธฐ ๋“œ๋กญ๋‹ค์šด ๋ฐ์ดํ„ฐ ๋กœ๋“œ: {bg_type}")
94
- else:
95
- # API ํ˜ธ์ถœ๋กœ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ
96
- if self.client:
97
- result = self.client.predict(bg_type, api_name="/update_dropdowns")
98
- self.background_options[bg_type] = result # ์บ์‹œ์— ์ €์žฅ
99
- logger.info(f"API์—์„œ ์ดˆ๊ธฐ ๋“œ๋กญ๋‹ค์šด ๋ฐ์ดํ„ฐ ๋กœ๋“œ: {bg_type}")
100
- else:
101
- logger.error("ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ดˆ๊ธฐํ™”๋˜์ง€ ์•Š์Œ")
102
- return tuple([[] for _ in range(7)])
103
-
104
- # ๊ฒฐ๊ณผ ๋ฐ˜ํ™˜ (์‹ค์ œ ์„ ํƒ์ง€๋งŒ)
105
- if isinstance(result, (list, tuple)) and len(result) >= 7:
106
- return result[:7]
107
- else:
108
- return tuple([[] for _ in range(7)])
109
-
110
- except Exception as e:
111
- logger.error(f"์ดˆ๊ธฐ ๋“œ๋กญ๋‹ค์šด ๋ฐ์ดํ„ฐ ๋กœ๋“œ ์‹คํŒจ: {str(e)}")
112
- return tuple([[] for _ in range(7)])
113
 
114
- def _parse_dropdown_result(self, result):
115
- """API ์‘๋‹ต์—์„œ ์‹ค์ œ ์„ ํƒ์ง€ ๋ฐ์ดํ„ฐ๋งŒ ์ถ”์ถœ - ๋‹จ์ˆœํ™”"""
116
- try:
117
- logger.info(f"์‘๋‹ต ํŒŒ์‹ฑ: {result}")
118
-
119
- # API ๋ฌธ์„œ์— ๋”ฐ๋ฅด๋ฉด tuple of 7 elements๋ฅผ ๋ฐ˜ํ™˜
120
- # ๊ฐ ์š”์†Œ๋Š” Literal ํƒ€์ž…์˜ ๋ฆฌ์ŠคํŠธ์—ฌ์•ผ ํ•จ
121
- if isinstance(result, (list, tuple)) and len(result) >= 7:
122
- # ๊ฒฐ๊ณผ๋ฅผ ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜ (API๊ฐ€ ์ด๋ฏธ ์˜ฌ๋ฐ”๋ฅธ ํ˜•์‹์œผ๋กœ ๋ฐ˜ํ™˜ํ•œ๋‹ค๊ณ  ๊ฐ€์ •)
123
- return result[:7]
124
- else:
125
- logger.warning(f"์˜ˆ์ƒํ•˜์ง€ ๋ชปํ•œ ์‘๋‹ต ํ˜•์‹: {type(result)}, length={len(result) if hasattr(result, '__len__') else 'N/A'}")
126
- return [[] for _ in range(7)]
127
-
128
- except Exception as e:
129
- logger.error(f"๋“œ๋กญ๋‹ค์šด ๊ฒฐ๊ณผ ํŒŒ์‹ฑ ์˜ค๋ฅ˜: {str(e)}")
130
- return [[] for _ in range(7)]
131
 
132
- def get_initial_simple_choices(self) -> list:
133
- """์‹ฌํ”Œ ๋ฐฐ๊ฒฝ ์ดˆ๊ธฐ ์„ ํƒ์ง€ - ํ•˜๋“œ์ฝ”๋”ฉ์œผ๋กœ ํ…Œ์ŠคํŠธ"""
134
- # API ๋ฌธ์„œ์—์„œ ํ™•์ธ๋œ ์‹ค์ œ ์„ ํƒ์ง€๋“ค
135
- return [
136
- 'ํ™”์ดํŠธ ๊ธฐ๋ณธ', 'ํšŒ์ƒ‰ ํˆฌํ†ค', '๋ผ์ดํŠธ ๊ทธ๋ ˆ์ด', '๊ทธ๋ ˆ์ด ๊ทธ๋ผ๋ฐ์ด์…˜ ์ŠคํฌํŠธ๋ผ์ดํŠธ',
137
- 'ํ”„๋ฆฌ๋ฏธ์—„ ๋“œ๋ผ๋งˆํ‹ฑ ๋ธ”๋ž™', '๋”ฅ๋ธ”๋ฃจ ์œ ๋ฆฌ๋ฐ˜์‚ฌ', 'ํŒŒ์Šคํ…” ๊ทธ๋ผ๋ฐ์ด์…˜', '์Šค์นด์ด๋ธ”๋ฃจ ํŒŒ์Šคํ…”',
138
- '๋ฒ„ํ„ฐ์˜๋กœ์šฐ ํŒŒ์Šคํ…”', '๋ธ”๋ฃจ ์›์ƒ‰', '๋ ˆ๋“œ ์›์ƒ‰'
139
- ]
140
-
141
- def _ensure_client(self) -> bool:
142
- """ํด๋ผ์ด์–ธํŠธ ์—ฐ๊ฒฐ ์ƒํƒœ ํ™•์ธ ๋ฐ ์žฌ์—ฐ๊ฒฐ"""
143
- if self.client is None:
144
- logger.info("ํด๋ผ์ด์–ธํŠธ ์žฌ์—ฐ๊ฒฐ ์‹œ๋„")
145
- self._initialize_client()
146
- return self.client is not None
147
-
148
- def update_dropdowns(self, bg_type: str) -> Tuple:
149
- """๋ฐฐ๊ฒฝ ์œ ํ˜•์— ๋”ฐ๋ฅธ ๋“œ๋กญ๋‹ค์šด ์—…๋ฐ์ดํŠธ - ๋‹จ์ˆœํ™”"""
150
- try:
151
- logger.info(f"๋“œ๋กญ๋‹ค์šด ์—…๋ฐ์ดํŠธ: {bg_type}")
152
-
153
- if not self._ensure_client():
154
- logger.error("ํด๏ฟฝ๏ฟฝ์ด์–ธํŠธ ์—ฐ๊ฒฐ ์‹คํŒจ")
155
- return tuple([gr.update(visible=False) for _ in range(7)])
156
-
157
- # API ํ˜ธ์ถœ
158
- result = self.client.predict(bg_type, api_name="/update_dropdowns")
159
- logger.info(f"API ์‘๋‹ต: {result}")
160
-
161
- # API ์‘๋‹ต์„ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉ
162
- if isinstance(result, (list, tuple)) and len(result) >= 7:
163
- updates = []
164
- visibility_map = ["์‹ฌํ”Œ ๋ฐฐ๊ฒฝ", "์ŠคํŠœ๋””์˜ค ๋ฐฐ๊ฒฝ", "์ž์—ฐ ํ™˜๊ฒฝ", "์‹ค๋‚ด ํ™˜๊ฒฝ", "ํŠน์ˆ˜๋ฐฐ๊ฒฝ", "์ฃผ์–ผ๋ฆฌ", "ํŠน์ˆ˜ํšจ๊ณผ"]
165
-
166
- for i, choices in enumerate(result[:7]):
167
- is_visible = (bg_type == visibility_map[i])
168
-
169
- if is_visible and isinstance(choices, list) and len(choices) > 0:
170
- logger.info(f"{visibility_map[i]} ๋“œ๋กญ๋‹ค์šด: {len(choices)}๊ฐœ ์„ ํƒ์ง€")
171
- updates.append(gr.update(
172
- visible=True,
173
- choices=choices,
174
- value=choices[0], # ์ฒซ ๋ฒˆ์งธ ํ•ญ๋ชฉ ์„ ํƒ
175
- interactive=True
176
- ))
177
- else:
178
- updates.append(gr.update(visible=False))
179
-
180
- return tuple(updates)
181
- else:
182
- logger.error(f"์ž˜๋ชป๋œ API ์‘๋‹ต: {result}")
183
- return tuple([gr.update(visible=False) for _ in range(7)])
184
-
185
- except Exception as e:
186
- logger.error(f"๋“œ๋กญ๋‹ค์šด ์—…๋ฐ์ดํŠธ ์˜ค๋ฅ˜: {str(e)}")
187
- return tuple([gr.update(visible=False) for _ in range(7)])
188
-
189
- def generate_prompt_only(self, password: str, bg_type: str, simple: str, studio: str,
190
- nature: str, indoor: str, special: str, jewelry: str,
191
- special_effects: str, request_text: str, aspect_ratio: str) -> str:
192
- """ํ”„๋กฌํ”„ํŠธ๋งŒ ์ƒ์„ฑ"""
193
- try:
194
- if not self._ensure_client():
195
- return "ํด๋ผ์ด์–ธํŠธ ์—ฐ๊ฒฐ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. ์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”."
196
-
197
- result = self.client.predict(
198
- password,
199
- bg_type,
200
- simple or "",
201
- studio or "",
202
- nature or "",
203
- indoor or "",
204
- special or "",
205
- jewelry or "",
206
- special_effects or "",
207
- request_text or "",
208
- aspect_ratio,
209
- api_name="/generate_prompt_with_password_check"
210
- )
211
-
212
- logger.info("ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ ์™„๋ฃŒ")
213
- return result if result else "ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค."
214
-
215
- except Exception as e:
216
- logger.error(f"ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ ์˜ค๋ฅ˜: {str(e)}")
217
- return f"ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ ์˜ค๋ฅ˜: {str(e)}"
218
-
219
- def process_image(self, password: str, image, bg_type: str, simple: str, studio: str,
220
- nature: str, indoor: str, special: str, jewelry: str, special_effects: str,
221
- request_text: str, quality_level: str, aspect_ratio: str,
222
- output_format: str, enable_enhancement: bool) -> Tuple:
223
- """์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ (ํŽธ์ง‘ ๋ฐ ํ™”์งˆ ๊ฐœ์„ ) - API ๋ฌธ์„œ์™€ ์ •ํ™•ํžˆ ๋งค์นญ"""
224
- temp_path = None
225
- try:
226
- if not self._ensure_client():
227
- return ([], None, [], None, "", "", "ํด๋ผ์ด์–ธํŠธ ์—ฐ๊ฒฐ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. ์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”.")
228
-
229
- # ์ด๋ฏธ์ง€ ๊ฒ€์ฆ
230
- if image is None:
231
- return ([], None, [], None, "", "", "์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•ด์ฃผ์„ธ์š”.")
232
-
233
- # ์ด๋ฏธ์ง€๋ฅผ ์ž„์‹œ ํŒŒ์ผ๋กœ ์ €์žฅ
234
- temp_path = tempfile.mktemp(suffix='.png')
235
- try:
236
- image.save(temp_path, format='PNG')
237
- logger.info(f"์ด๋ฏธ์ง€๋ฅผ ์ž„์‹œ ํŒŒ์ผ๋กœ ์ €์žฅ: {os.path.basename(temp_path)}")
238
- except Exception as e:
239
- logger.error(f"์ด๋ฏธ์ง€ ์ €์žฅ ์‹คํŒจ: {str(e)}")
240
- return ([], None, [], None, "", "", "์ด๋ฏธ์ง€ ์ €์žฅ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ์ด๋ฏธ์ง€๋ฅผ ์‹œ๋„ํ•ด๋ณด์„ธ์š”.")
241
-
242
- # API ํ˜ธ์ถœ - ๋งค๊ฐœ๋ณ€์ˆ˜ ์ˆœ์„œ๋ฅผ API ๋ฌธ์„œ์™€ ์ •ํ™•ํžˆ ๋งž์ถค
243
- logger.info("์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ API ํ˜ธ์ถœ ์‹œ์ž‘")
244
- result = self.client.predict(
245
- password, # password (str)
246
- handle_file(temp_path), # param_1 (image file)
247
- bg_type, # param_2 (๋ฐฐ๊ฒฝ ์œ ํ˜•)
248
- simple or "", # param_3 (์‹ฌํ”Œ ๋ฐฐ๊ฒฝ)
249
- studio or "", # param_4 (์ŠคํŠœ๋””์˜ค ๋ฐฐ๊ฒฝ)
250
- nature or "", # param_5 (์ž์—ฐ ํ™˜๊ฒฝ)
251
- indoor or "", # param_6 (์‹ค๋‚ด ํ™˜๊ฒฝ)
252
- special or "", # param_7 (ํŠน์ˆ˜๋ฐฐ๊ฒฝ)
253
- jewelry or "", # param_8 (์ฃผ์–ผ๋ฆฌ)
254
- special_effects or "", # param_9 (ํŠน์ˆ˜ํšจ๊ณผ)
255
- request_text or "", # param_10 (์š”์ฒญ์‚ฌํ•ญ)
256
- quality_level, # param_11 (ํ’ˆ์งˆ ๋ ˆ๋ฒจ)
257
- aspect_ratio, # param_12 (์ข…ํšก๋น„)
258
- output_format, # param_13 (์ด๋ฏธ์ง€ ํ˜•์‹)
259
- enable_enhancement, # param_14 (์ถ”๊ฐ€ ํ™”์งˆ ๊ฐœ์„ )
260
- api_name="/check_password"
261
- )
262
-
263
- logger.info("์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ API ํ˜ธ์ถœ ์™„๋ฃŒ")
264
-
265
- # ๊ฒฐ๊ณผ ๊ฒ€์ฆ ๋ฐ ๋ฐ˜ํ™˜ - API ๋ฌธ์„œ์— ๋”ฐ๋ฅด๋ฉด tuple of 7 elements
266
- if isinstance(result, (list, tuple)) and len(result) >= 7:
267
- # [0] original_gallery, [1] original_file, [2] enhanced_gallery,
268
- # [3] enhanced_file, [4] prompt_text, [5] info_text, [6] error_text
269
- return result[:7] # ์ •ํ™•ํžˆ 7๊ฐœ ์š”์†Œ๋งŒ ๋ฐ˜ํ™˜
270
- else:
271
- logger.warning(f"API ์‘๋‹ต ํ˜•์‹ ์ด์ƒ: type={type(result)}, length={len(result) if hasattr(result, '__len__') else 'N/A'}")
272
- return ([], None, [], None, "", "", "API์—์„œ ์˜ฌ๋ฐ”๋ฅด์ง€ ์•Š์€ ์‘๋‹ต์„ ๋ฐ›์•˜์Šต๋‹ˆ๋‹ค.")
273
-
274
- except Exception as e:
275
- error_msg = str(e)
276
- logger.error(f"์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ ์˜ค๋ฅ˜: {error_msg}")
277
-
278
- # ์ผ๋ฐ˜์ ์ธ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€ ๊ฐœ์„ 
279
- if "timeout" in error_msg.lower():
280
- user_error = "์š”์ฒญ ์ฒ˜๋ฆฌ ์‹œ๊ฐ„์ด ์ดˆ๊ณผ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”."
281
- elif "connection" in error_msg.lower():
282
- user_error = "์„œ๋ฒ„ ์—ฐ๊ฒฐ์— ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๋„คํŠธ์›Œํฌ ์ƒํƒœ๋ฅผ ํ™•์ธํ•ด์ฃผ์„ธ์š”."
283
- elif "unauthorized" in error_msg.lower() or "password" in error_msg.lower():
284
- user_error = "์ธ์ฆ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค. ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ํ™•์ธํ•ด์ฃผ์„ธ์š”."
285
- else:
286
- user_error = f"์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค: {error_msg}"
287
-
288
- return ([], None, [], None, "", "", user_error)
289
-
290
- finally:
291
- # ์ž„์‹œ ํŒŒ์ผ ์ •๋ฆฌ
292
- if temp_path and os.path.exists(temp_path):
293
- try:
294
- os.remove(temp_path)
295
- logger.info("์ž„์‹œ ํŒŒ์ผ ์ •๋ฆฌ ์™„๋ฃŒ")
296
- except Exception as e:
297
- logger.warning(f"์ž„์‹œ ํŒŒ์ผ ์ •๋ฆฌ ์‹คํŒจ: {str(e)}")
298
 
299
- # ์ „์—ญ ํด๋ผ์ด์–ธํŠธ ์ปจํŠธ๋กค๋Ÿฌ ์ธ์Šคํ„ด์Šค
300
- controller = GradioClientController()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
301
 
 
302
  def create_gradio_interface():
303
- """Gradio ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ"""
304
  try:
305
- logger.info("Gradio ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ ์‹œ์ž‘")
306
-
307
- # ๋ฐฐ๊ฒฝ ์œ ํ˜• ์„ ํƒ์ง€
308
- background_types = ["์‹ฌํ”Œ ๋ฐฐ๊ฒฝ", "์ŠคํŠœ๋””์˜ค ๋ฐฐ๊ฒฝ", "์ž์—ฐ ํ™˜๊ฒฝ", "์‹ค๋‚ด ํ™˜๊ฒฝ", "ํŠน์ˆ˜๋ฐฐ๊ฒฝ", "์ฃผ์–ผ๋ฆฌ", "ํŠน์ˆ˜ํšจ๊ณผ"]
309
-
310
- # ์ปค์Šคํ…€ CSS ์ถ”๊ฐ€ (์›๋ณธ๊ณผ ๋™์ผํ•œ ์Šค๏ฟฝ๏ฟฝ์ผ ์œ ์ง€)
311
- css = """
312
- .gradio-container {
313
- max-width: 1200px !important;
314
- }
315
- """
316
-
317
- with gr.Blocks(title="AI ์ด๋ฏธ์ง€ ํŽธ์ง‘ ๋ฐ ํ™”์งˆ ๊ฐœ์„ ", css=css, theme=gr.themes.Soft()) as app:
318
  gr.Markdown("# AI ์ด๋ฏธ์ง€ ํŽธ์ง‘ ๋ฐ ํ™”์งˆ ๊ฐœ์„  ๋„๊ตฌ")
319
 
320
  # ๋น„๋ฐ€๋ฒˆํ˜ธ ์ž…๋ ฅ ํ•„๋“œ
@@ -334,61 +161,85 @@ def create_gradio_interface():
334
  with gr.Row():
335
  with gr.Column():
336
  background_type = gr.Radio(
337
- choices=background_types,
338
  label="๋ฐฐ๊ฒฝ ์œ ํ˜•",
339
  value="์‹ฌํ”Œ ๋ฐฐ๊ฒฝ"
340
  )
341
 
342
- # ๋“œ๋กญ๋‹ค์šด ์ปดํฌ๋„ŒํŠธ๋“ค - ์‹ฌํ”Œ ๋ฐฐ๊ฒฝ์€ ํ•˜๋“œ์ฝ”๋”ฉ๋œ ์„ ํƒ์ง€๋กœ ์‹œ์ž‘
343
  simple_dropdown = gr.Dropdown(
344
- choices=controller.get_initial_simple_choices(),
345
- value="ํ™”์ดํŠธ ๊ธฐ๋ณธ", # ๊ธฐ๋ณธ๊ฐ’ ์„ค์ •
346
  label="์‹ฌํ”Œ ๋ฐฐ๊ฒฝ ์„ ํƒ",
347
  visible=True,
348
  interactive=True
349
  )
350
 
351
  studio_dropdown = gr.Dropdown(
352
- choices=[],
 
353
  label="์ŠคํŠœ๋””์˜ค ๋ฐฐ๊ฒฝ ์„ ํƒ",
354
  visible=False,
355
  interactive=True
356
  )
357
 
358
  nature_dropdown = gr.Dropdown(
359
- choices=[],
 
360
  label="์ž์—ฐ ํ™˜๊ฒฝ ์„ ํƒ",
361
  visible=False,
362
  interactive=True
363
  )
364
 
365
  indoor_dropdown = gr.Dropdown(
366
- choices=[],
 
367
  label="์‹ค๋‚ด ํ™˜๊ฒฝ ์„ ํƒ",
368
  visible=False,
369
  interactive=True
370
  )
371
 
372
  special_dropdown = gr.Dropdown(
373
- choices=[],
 
374
  label="ํŠน์ˆ˜๋ฐฐ๊ฒฝ ์„ ํƒ",
375
  visible=False,
376
  interactive=True
377
  )
378
 
379
  jewelry_dropdown = gr.Dropdown(
380
- choices=[],
 
381
  label="์ฃผ์–ผ๋ฆฌ ๋ฐฐ๊ฒฝ ์„ ํƒ",
382
  visible=False,
383
  interactive=True
384
  )
385
 
386
  special_effects_dropdown = gr.Dropdown(
387
- choices=[],
 
388
  label="ํŠน์ˆ˜ํšจ๊ณผ ๋ฐฐ๊ฒฝ ์„ ํƒ",
389
  visible=False,
390
  interactive=True
391
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
392
 
393
  # ์š”์ฒญ์‚ฌํ•ญ ์ž…๋ ฅ
394
  request_text = gr.Textbox(
@@ -402,7 +253,7 @@ def create_gradio_interface():
402
  label="ํ’ˆ์งˆ ๋ ˆ๋ฒจ",
403
  choices=["gpt", "flux"],
404
  value="flux",
405
- info="GPT: GPT ๋ชจ๋ธ (๊ณ ํ’ˆ์งˆ), Flux: Flux ๋ชจ๋ธ (๋น ๋ฅธ ์ฒ˜๋ฆฌ + ๊ธฐ๋ณธ ํ™”์งˆ๊ฐœ์„ )"
406
  )
407
 
408
  aspect_ratio = gr.Dropdown(
@@ -423,12 +274,13 @@ def create_gradio_interface():
423
  value=False,
424
  info="GPT: 1ํšŒ ํ™”์งˆ๊ฐœ์„ , Flux: 2์ฐจ ํ™”์งˆ๊ฐœ์„  (๊ธฐ๋ณธ 1ํšŒ + ์ถ”๊ฐ€ 1ํšŒ)"
425
  )
 
426
 
427
  # ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ ๋ฒ„ํŠผ
428
- generate_prompt_btn = gr.Button("ํ”„๋กฌํ”„ํŠธ๋งŒ ์ƒ์„ฑ", variant="secondary")
429
 
430
  # ํŽธ์ง‘ ๋ฒ„ํŠผ
431
- edit_btn = gr.Button("์ด๋ฏธ์ง€ ํŽธ์ง‘ ๋ฐ ํ™”์งˆ ๊ฐœ์„ ", variant="primary")
432
 
433
  with gr.Column():
434
  with gr.Row():
@@ -447,18 +299,40 @@ def create_gradio_interface():
447
  info = gr.Textbox(label="์ฒ˜๋ฆฌ ์ •๋ณด", interactive=False)
448
  error = gr.Textbox(label="์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€", interactive=False, visible=True)
449
 
450
- # ๋“œ๋กญ๋‹ค์šด ๋ณ€๊ฒฝ ์ด๋ฒคํŠธ - ์ฆ‰์‹œ ์—…๋ฐ์ดํŠธ
451
- background_type.change(
452
- fn=controller.update_dropdowns,
453
- inputs=[background_type],
454
- outputs=[simple_dropdown, studio_dropdown, nature_dropdown,
455
- indoor_dropdown, special_dropdown, jewelry_dropdown, special_effects_dropdown],
456
- queue=False # ํ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•„ ๋” ๋น ๋ฅธ ์‘๋‹ต
457
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
458
 
459
  # ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ ๋ฒ„ํŠผ ํด๋ฆญ ์ด๋ฒคํŠธ
460
  generate_prompt_btn.click(
461
- fn=controller.generate_prompt_only,
462
  inputs=[
463
  password_box,
464
  background_type,
@@ -471,7 +345,7 @@ def create_gradio_interface():
471
 
472
  # ํŽธ์ง‘ ๋ฒ„ํŠผ ํด๋ฆญ ์ด๋ฒคํŠธ
473
  edit_btn.click(
474
- fn=controller.process_image,
475
  inputs=[
476
  password_box,
477
  image, background_type,
@@ -485,63 +359,99 @@ def create_gradio_interface():
485
  prompt_output, info, error
486
  ]
487
  )
488
-
489
- # ์•ฑ ๋กœ๋“œ ์‹œ ๋ชจ๋“  ๋“œ๋กญ๋‹ค์šด ์ดˆ๊ธฐ ์„ค์ • (๋” ๊ฐ•๋ ฅํ•œ ์ดˆ๊ธฐํ™”)
490
- def initialize_all_dropdowns():
491
- """๋ชจ๋“  ๋“œ๋กญ๋‹ค์šด์„ ์ดˆ๊ธฐํ™”ํ•˜๋Š” ํ•จ์ˆ˜"""
492
- try:
493
- logger.info("๋“œ๋กญ๋‹ค์šด ์ดˆ๊ธฐํ™” ์‹œ์ž‘")
494
- updates = controller.update_dropdowns("์‹ฌํ”Œ ๋ฐฐ๊ฒฝ")
495
- logger.info("๋“œ๋กญ๋‹ค์šด ์ดˆ๊ธฐํ™” ์™„๋ฃŒ")
496
- return updates
497
- except Exception as e:
498
- logger.error(f"๋“œ๋กญ๋‹ค์šด ์ดˆ๊ธฐํ™” ์‹คํŒจ: {str(e)}")
499
- return tuple([gr.update() for _ in range(7)])
500
-
501
- app.load(
502
- fn=initialize_all_dropdowns,
503
- outputs=[simple_dropdown, studio_dropdown, nature_dropdown,
504
- indoor_dropdown, special_dropdown, jewelry_dropdown, special_effects_dropdown]
505
- )
506
 
507
- logger.info("Gradio ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ ์™„๋ฃŒ")
508
  return app
509
-
510
  except Exception as e:
511
- logger.error(f"Gradio ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ ์˜ค๋ฅ˜: {str(e)}")
512
  logger.error(traceback.format_exc())
513
  raise
514
 
515
  # ์•ฑ ์‹คํ–‰
516
  if __name__ == "__main__":
517
  try:
518
- logger.info("์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹œ์ž‘")
519
 
520
  # imgs ๋””๋ ‰ํ† ๋ฆฌ ํ™•์ธ/์ƒ์„ฑ
521
  os.makedirs("imgs", exist_ok=True)
522
  logger.info("์ด๋ฏธ์ง€ ๋””๋ ‰ํ† ๋ฆฌ ์ค€๋น„ ์™„๋ฃŒ")
523
 
524
- # API ์—”๋“œํฌ์ธํŠธ ํ™•์ธ
525
- if not os.environ.get("API_ENDPOINT"):
526
- logger.error("API_ENDPOINT ํ™˜๊ฒฝ ๋ณ€์ˆ˜๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.")
527
- print("\nโŒ ์˜ค๋ฅ˜: API_ENDPOINT ํ™˜๊ฒฝ ๋ณ€์ˆ˜๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.")
528
- print("ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ์„ค์ •ํ•˜๊ฑฐ๋‚˜ .env ํŒŒ์ผ์— API_ENDPOINT๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ์„ธ์š”.")
529
- sys.exit(1)
530
-
531
  app = create_gradio_interface()
532
- logger.info("Gradio ์•ฑ ์‹œ์ž‘")
533
- app.launch(
534
- share=True,
535
- server_name="0.0.0.0",
536
- server_port=7860,
537
- show_error=True,
538
- quiet=False
539
- )
 
 
540
 
541
- except KeyboardInterrupt:
542
- logger.info("์‚ฌ์šฉ์ž์— ์˜ํ•ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์ค‘๋‹จ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.")
 
 
 
 
 
 
 
543
  except Exception as e:
544
- logger.error(f"์•ฑ ์‹คํ–‰ ์˜ค๋ฅ˜: {str(e)}")
545
- logger.error(traceback.format_exc())
546
- print(f"\nโŒ ์‹ฌ๊ฐํ•œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค: {str(e)}")
547
- print("์ž์„ธํ•œ ๋‚ด์šฉ์€ app.log ํŒŒ์ผ์„ ํ™•์ธํ•ด์ฃผ์„ธ์š”.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import os
2
  import sys
3
+ import base64
4
+ import io
5
  import logging
6
  import tempfile
7
  import traceback
8
+ import requests
9
+ from PIL import Image
10
  import gradio as gr
11
+ from gradio_client import Client
12
  from dotenv import load_dotenv
 
13
 
14
+ # ํ™˜๊ฒฝ๋ณ€์ˆ˜ ๋กœ๋“œ
15
  load_dotenv()
16
 
17
+ # ๋กœ๊น… ์„ค์ • (๋ฏผ๊ฐํ•œ ์ •๋ณด ์ œ์™ธ)
18
+ logging.basicConfig(
19
+ level=logging.INFO,
20
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
21
+ handlers=[
22
+ logging.FileHandler("app.log"),
23
+ logging.StreamHandler(sys.stdout)
24
+ ]
25
+ )
26
+ logger = logging.getLogger("image-enhancer-control-tower")
27
 
28
+ # API ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐํ™” (ํ™˜๊ฒฝ๋ณ€์ˆ˜์—์„œ)
29
+ API_ENDPOINT = os.environ.get("API_ENDPOINT")
30
+ if not API_ENDPOINT:
31
+ logger.error("API_ENDPOINT environment variable is not set")
32
+ sys.exit(1)
 
33
 
34
+ try:
35
+ # ๋กœ๊ทธ์— ์—”๋“œํฌ์ธํŠธ ์ •๋ณด ์ถœ๋ ฅํ•˜์ง€ ์•Š์Œ
36
+ client = Client(API_ENDPOINT)
37
+ logger.info("API client initialized successfully")
38
+ except Exception as e:
39
+ logger.error(f"Failed to initialize API client: {e}")
40
+ sys.exit(1)
41
 
42
+ # backgrounds.py ์ „์ฒด ๋‚ด์šฉ์„ ํ™˜๊ฒฝ๋ณ€์ˆ˜์—์„œ ๊ฐ€์ ธ์˜ค๊ธฐ
43
+ def load_backgrounds_from_env():
44
+ """ํ™˜๊ฒฝ๋ณ€์ˆ˜์—์„œ backgrounds.py ์ „์ฒด ๋‚ด์šฉ ๋กœ๋“œ"""
45
+ backgrounds_code = os.environ.get("BACKGROUNDS_DATA", "")
46
+ if not backgrounds_code:
47
+ logger.warning("BACKGROUNDS_DATA environment variable is empty")
 
 
48
 
49
+ with tempfile.NamedTemporaryFile(delete=False, suffix=suffix) as temp_file:
50
+ temp_filename = temp_file.name
51
+ logger.info(f"Created temporary file: {temp_filename}")
52
+
53
+ # Gradio ์—…๋กœ๋“œ ํŒŒ์ผ ์ฒ˜๋ฆฌ
54
+ if isinstance(uploaded_file, str): # ์ด๋ฏธ ํŒŒ์ผ ๊ฒฝ๋กœ์ธ ๊ฒฝ์šฐ
55
+ logger.info(f"Uploaded file is already a path: {uploaded_file}")
56
+ return uploaded_file
57
+
58
+ # PIL Image ์ฒ˜๋ฆฌ
59
+ if isinstance(uploaded_file, Image.Image):
60
+ logger.info("Uploaded file is a PIL Image")
61
+ uploaded_file.save(temp_filename, format="PNG")
62
+ return temp_filename
63
+
64
+ # ๋ฐ”์ด๋„ˆ๋ฆฌ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ
65
+ with open(temp_filename, "wb") as f:
66
+ if hasattr(uploaded_file, "read"): # ํŒŒ์ผ ๊ฐ์ฒด์ธ ๊ฒฝ์šฐ
67
+ logger.info("Processing file object")
68
+ content = uploaded_file.read()
69
+ f.write(content)
70
+ logger.info(f"Wrote {len(content)} bytes to {temp_filename}")
71
+ else: # ๋ฐ”์ด๋„ˆ๋ฆฌ ๋ฐ์ดํ„ฐ์ธ ๊ฒฝ์šฐ
72
+ logger.info("Processing binary data")
73
+ f.write(uploaded_file)
74
+ logger.info(f"Wrote data to {temp_filename}")
75
+
76
+ return temp_filename
77
+ except Exception as e:
78
+ logger.error(f"Error saving uploaded file: {e}")
79
+ logger.error(traceback.format_exc())
80
+ return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
 
82
+ # API ํ˜ธ์ถœ ํ•จ์ˆ˜๋“ค (ํด๋ผ์ด์–ธํŠธ๋ฅผ ํ†ตํ•ด ์›๋ณธ API ํ˜ธ์ถœ)
83
+ def api_update_dropdowns(bg_type):
84
+ """๋“œ๋กญ๋‹ค์šด ์—…๋ฐ์ดํŠธ API ํ˜ธ์ถœ"""
85
+ try:
86
+ result = client.predict(bg_type=bg_type, api_name="/update_dropdowns")
87
+ return result
88
+ except Exception as e:
89
+ logger.error(f"Error calling update_dropdowns API: {e}")
90
+ return None
 
 
 
 
 
 
 
 
91
 
92
+ def api_generate_prompt_with_password_check(password, bg_type, simple, studio, nature, indoor, special, jewelry, special_effects, request_text, aspect_ratio):
93
+ """ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ API ํ˜ธ์ถœ"""
94
+ try:
95
+ result = client.predict(
96
+ password=password,
97
+ bg_type=bg_type,
98
+ simple=simple,
99
+ studio=studio,
100
+ nature=nature,
101
+ indoor=indoor,
102
+ special=special,
103
+ jewelry=jewelry,
104
+ special_effects=special_effects,
105
+ request_text=request_text,
106
+ aspect_ratio=aspect_ratio,
107
+ api_name="/generate_prompt_with_password_check"
108
+ )
109
+ return result
110
+ except Exception as e:
111
+ logger.error(f"Error calling generate_prompt API: {e}")
112
+ return f"API ํ˜ธ์ถœ ์˜ค๋ฅ˜: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
 
114
+ def api_check_password(password, image_file, bg_type, simple, studio, nature, indoor, special, jewelry, special_effects, request_text, quality_level, aspect_ratio, output_format, enable_enhancement):
115
+ """์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ API ํ˜ธ์ถœ"""
116
+ try:
117
+ result = client.predict(
118
+ password=password,
119
+ param_1=image_file,
120
+ param_2=bg_type,
121
+ param_3=simple,
122
+ param_4=studio,
123
+ param_5=nature,
124
+ param_6=indoor,
125
+ param_7=special,
126
+ param_8=jewelry,
127
+ param_9=special_effects,
128
+ param_10=request_text,
129
+ param_11=quality_level,
130
+ param_12=aspect_ratio,
131
+ param_13=output_format,
132
+ param_14=enable_enhancement,
133
+ api_name="/check_password"
134
+ )
135
+ return result
136
+ except Exception as e:
137
+ logger.error(f"Error calling check_password API: {e}")
138
+ return None, None, None, None, "", "", f"API ํ˜ธ์ถœ ์˜ค๋ฅ˜: {str(e)}"
139
 
140
+ # Gradio ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌ์„ฑ (์™„์ „ํžˆ ๋™์ผํ•œ UI)
141
  def create_gradio_interface():
 
142
  try:
143
+ logger.info("Creating Gradio interface")
144
+ with gr.Blocks(title="AI ์ด๋ฏธ์ง€ ํŽธ์ง‘ ๋ฐ ํ™”์งˆ ๊ฐœ์„ ") as app:
 
 
 
 
 
 
 
 
 
 
 
145
  gr.Markdown("# AI ์ด๋ฏธ์ง€ ํŽธ์ง‘ ๋ฐ ํ™”์งˆ ๊ฐœ์„  ๋„๊ตฌ")
146
 
147
  # ๋น„๋ฐ€๋ฒˆํ˜ธ ์ž…๋ ฅ ํ•„๋“œ
 
161
  with gr.Row():
162
  with gr.Column():
163
  background_type = gr.Radio(
164
+ choices=["์‹ฌํ”Œ ๋ฐฐ๊ฒฝ", "์ŠคํŠœ๋””์˜ค ๋ฐฐ๊ฒฝ", "์ž์—ฐ ํ™˜๊ฒฝ", "์‹ค๋‚ด ํ™˜๊ฒฝ", "ํŠน์ˆ˜๋ฐฐ๊ฒฝ", "์ฃผ์–ผ๋ฆฌ", "ํŠน์ˆ˜ํšจ๊ณผ"],
165
  label="๋ฐฐ๊ฒฝ ์œ ํ˜•",
166
  value="์‹ฌํ”Œ ๋ฐฐ๊ฒฝ"
167
  )
168
 
169
+ # ๋“œ๋กญ๋‹ค์šด ์ปดํฌ๋„ŒํŠธ๋“ค
170
  simple_dropdown = gr.Dropdown(
171
+ choices=list(SIMPLE_BACKGROUNDS.keys()) if SIMPLE_BACKGROUNDS else ["ํ™”์ดํŠธ ๊ธฐ๋ณธ"],
172
+ value=list(SIMPLE_BACKGROUNDS.keys())[0] if SIMPLE_BACKGROUNDS else "ํ™”์ดํŠธ ๊ธฐ๋ณธ",
173
  label="์‹ฌํ”Œ ๋ฐฐ๊ฒฝ ์„ ํƒ",
174
  visible=True,
175
  interactive=True
176
  )
177
 
178
  studio_dropdown = gr.Dropdown(
179
+ choices=list(STUDIO_BACKGROUNDS.keys()) if STUDIO_BACKGROUNDS else ["์—ฐ๋…น์ƒ‰ ์žฅ๋ฏธ ์ •์›"],
180
+ value=list(STUDIO_BACKGROUNDS.keys())[0] if STUDIO_BACKGROUNDS else "์—ฐ๋…น์ƒ‰ ์žฅ๋ฏธ ์ •์›",
181
  label="์ŠคํŠœ๋””์˜ค ๋ฐฐ๊ฒฝ ์„ ํƒ",
182
  visible=False,
183
  interactive=True
184
  )
185
 
186
  nature_dropdown = gr.Dropdown(
187
+ choices=list(NATURE_BACKGROUNDS.keys()) if NATURE_BACKGROUNDS else ["์ž‘์€ ํŒŒ๋„๊ฐ€ ์žˆ๋Š” ํ•ด๋ณ€"],
188
+ value=list(NATURE_BACKGROUNDS.keys())[0] if NATURE_BACKGROUNDS else "์ž‘์€ ํŒŒ๋„๊ฐ€ ์žˆ๋Š” ํ•ด๋ณ€",
189
  label="์ž์—ฐ ํ™˜๊ฒฝ ์„ ํƒ",
190
  visible=False,
191
  interactive=True
192
  )
193
 
194
  indoor_dropdown = gr.Dropdown(
195
+ choices=list(INDOOR_BACKGROUNDS.keys()) if INDOOR_BACKGROUNDS else ["๊ธฐ๋ณธ ์ฑ…์ƒ"],
196
+ value=list(INDOOR_BACKGROUNDS.keys())[0] if INDOOR_BACKGROUNDS else "๊ธฐ๋ณธ ์ฑ…์ƒ",
197
  label="์‹ค๋‚ด ํ™˜๊ฒฝ ์„ ํƒ",
198
  visible=False,
199
  interactive=True
200
  )
201
 
202
  special_dropdown = gr.Dropdown(
203
+ choices=list(SPECIAL_BACKGROUNDS.keys()) if SPECIAL_BACKGROUNDS else ["๋„ค์ด๋น„ ๋นˆํ‹ฐ์ง€ ํ”Œ๋กœ๋Ÿด ๋ฒฝ์ง€"],
204
+ value=list(SPECIAL_BACKGROUNDS.keys())[0] if SPECIAL_BACKGROUNDS else "๋„ค์ด๋น„ ๋นˆํ‹ฐ์ง€ ํ”Œ๋กœ๋Ÿด ๋ฒฝ์ง€",
205
  label="ํŠน์ˆ˜๋ฐฐ๊ฒฝ ์„ ํƒ",
206
  visible=False,
207
  interactive=True
208
  )
209
 
210
  jewelry_dropdown = gr.Dropdown(
211
+ choices=list(JEWELRY_BACKGROUNDS.keys()) if JEWELRY_BACKGROUNDS else ["ํ™”์ดํŠธ ๋ฏธ๋Ÿฌ ์ŠคํŒŸ ๋ผ์ดํŠธ"],
212
+ value=list(JEWELRY_BACKGROUNDS.keys())[0] if JEWELRY_BACKGROUNDS else "ํ™”์ดํŠธ ๋ฏธ๋Ÿฌ ์ŠคํŒŸ ๋ผ์ดํŠธ",
213
  label="์ฃผ์–ผ๋ฆฌ ๋ฐฐ๊ฒฝ ์„ ํƒ",
214
  visible=False,
215
  interactive=True
216
  )
217
 
218
  special_effects_dropdown = gr.Dropdown(
219
+ choices=list(SPECIAL_EFFECTS_BACKGROUNDS.keys()) if SPECIAL_EFFECTS_BACKGROUNDS else ["๋ธ”๋ฃจ๋ธ”๋ž™ ํฐ ๋ฌผ๋ฐฉ์šธ ํšจ๊ณผ"],
220
+ value=list(SPECIAL_EFFECTS_BACKGROUNDS.keys())[0] if SPECIAL_EFFECTS_BACKGROUNDS else "๋ธ”๋ฃจ๋ธ”๋ž™ ํฐ ๋ฌผ๋ฐฉ์šธ ํšจ๊ณผ",
221
  label="ํŠน์ˆ˜ํšจ๊ณผ ๋ฐฐ๊ฒฝ ์„ ํƒ",
222
  visible=False,
223
  interactive=True
224
  )
225
+
226
+ # ๋“œ๋กญ๋‹ค์šด ๋ณ€๊ฒฝ ํ•จ์ˆ˜
227
+ def update_dropdowns(bg_type):
228
+ return {
229
+ simple_dropdown: gr.update(visible=(bg_type == "์‹ฌํ”Œ ๋ฐฐ๊ฒฝ")),
230
+ studio_dropdown: gr.update(visible=(bg_type == "์ŠคํŠœ๋””์˜ค ๋ฐฐ๊ฒฝ")),
231
+ nature_dropdown: gr.update(visible=(bg_type == "์ž์—ฐ ํ™˜๊ฒฝ")),
232
+ indoor_dropdown: gr.update(visible=(bg_type == "์‹ค๋‚ด ํ™˜๊ฒฝ")),
233
+ special_dropdown: gr.update(visible=(bg_type == "ํŠน์ˆ˜๋ฐฐ๊ฒฝ")),
234
+ jewelry_dropdown: gr.update(visible=(bg_type == "์ฃผ์–ผ๋ฆฌ")),
235
+ special_effects_dropdown: gr.update(visible=(bg_type == "ํŠน์ˆ˜ํšจ๊ณผ"))
236
+ }
237
+
238
+ background_type.change(
239
+ fn=update_dropdowns,
240
+ inputs=[background_type],
241
+ outputs=[simple_dropdown, studio_dropdown, nature_dropdown, indoor_dropdown, special_dropdown, jewelry_dropdown, special_effects_dropdown]
242
+ )
243
 
244
  # ์š”์ฒญ์‚ฌํ•ญ ์ž…๋ ฅ
245
  request_text = gr.Textbox(
 
253
  label="ํ’ˆ์งˆ ๋ ˆ๋ฒจ",
254
  choices=["gpt", "flux"],
255
  value="flux",
256
+ info="GPT: GPT ๋ชจ๋ธ (๊ณ ํ’ˆ์งˆ), ์ผ๋ฐ˜: Flux ๋ชจ๋ธ (๋น ๋ฅธ ์ฒ˜๋ฆฌ + ๊ธฐ๋ณธ ํ™”์งˆ๊ฐœ์„ )"
257
  )
258
 
259
  aspect_ratio = gr.Dropdown(
 
274
  value=False,
275
  info="GPT: 1ํšŒ ํ™”์งˆ๊ฐœ์„ , Flux: 2์ฐจ ํ™”์งˆ๊ฐœ์„  (๊ธฐ๋ณธ 1ํšŒ + ์ถ”๊ฐ€ 1ํšŒ)"
276
  )
277
+ enhancement_level = gr.Slider(label="ํ™”์งˆ ๊ฐœ์„  ๋ ˆ๋ฒจ", minimum=1, maximum=4, value=2, step=1, visible=False)
278
 
279
  # ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ ๋ฒ„ํŠผ
280
+ generate_prompt_btn = gr.Button("ํ”„๋กฌํ”„ํŠธ๋งŒ ์ƒ์„ฑ")
281
 
282
  # ํŽธ์ง‘ ๋ฒ„ํŠผ
283
+ edit_btn = gr.Button("์ด๋ฏธ์ง€ ํŽธ์ง‘ ๋ฐ ํ™”์งˆ ๊ฐœ์„ ")
284
 
285
  with gr.Column():
286
  with gr.Row():
 
299
  info = gr.Textbox(label="์ฒ˜๋ฆฌ ์ •๋ณด", interactive=False)
300
  error = gr.Textbox(label="์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€", interactive=False, visible=True)
301
 
302
+ # ํ”„๋กฌํ”„ํŠธ๋งŒ ์ƒ์„ฑํ•˜๋Š” ํ•จ์ˆ˜ (API ํ˜ธ์ถœ)
303
+ def generate_prompt_wrapper(password, bg_type, simple, studio, nature, indoor, special, jewelry, special_effects, request_text, aspect_ratio):
304
+ return api_generate_prompt_with_password_check(
305
+ password, bg_type, simple, studio, nature, indoor, special, jewelry, special_effects, request_text, aspect_ratio
306
+ )
307
+
308
+ # ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ ํ•จ์ˆ˜ (API ํ˜ธ์ถœ)
309
+ def process_image_wrapper(password, image, bg_type, simple, studio, nature, indoor, special, jewelry, special_effects, request_text, quality_level, aspect_ratio, output_format, enable_enhancement):
310
+ if image is None:
311
+ return [], None, [], None, "", "", "์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค."
312
+
313
+ # ์ด๋ฏธ์ง€๋ฅผ ์ž„์‹œ ํŒŒ์ผ๋กœ ์ €์žฅ
314
+ temp_path = save_uploaded_file(image)
315
+ if temp_path is None:
316
+ return [], None, [], None, "", "", "์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค."
317
+
318
+ try:
319
+ # API ํ˜ธ์ถœ
320
+ result = api_check_password(
321
+ password, temp_path, bg_type, simple, studio, nature, indoor, special, jewelry, special_effects,
322
+ request_text, quality_level, aspect_ratio, output_format, enable_enhancement
323
+ )
324
+ return result
325
+ finally:
326
+ # ์ž„์‹œ ํŒŒ์ผ ์ •๋ฆฌ
327
+ if os.path.exists(temp_path):
328
+ try:
329
+ os.remove(temp_path)
330
+ except:
331
+ pass
332
 
333
  # ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ ๋ฒ„ํŠผ ํด๋ฆญ ์ด๋ฒคํŠธ
334
  generate_prompt_btn.click(
335
+ fn=generate_prompt_wrapper,
336
  inputs=[
337
  password_box,
338
  background_type,
 
345
 
346
  # ํŽธ์ง‘ ๋ฒ„ํŠผ ํด๋ฆญ ์ด๋ฒคํŠธ
347
  edit_btn.click(
348
+ fn=process_image_wrapper,
349
  inputs=[
350
  password_box,
351
  image, background_type,
 
359
  prompt_output, info, error
360
  ]
361
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
362
 
363
+ logger.info("Gradio interface created successfully")
364
  return app
 
365
  except Exception as e:
366
+ logger.error(f"Error creating Gradio interface: {e}")
367
  logger.error(traceback.format_exc())
368
  raise
369
 
370
  # ์•ฑ ์‹คํ–‰
371
  if __name__ == "__main__":
372
  try:
373
+ logger.info("Starting control tower application")
374
 
375
  # imgs ๋””๋ ‰ํ† ๋ฆฌ ํ™•์ธ/์ƒ์„ฑ
376
  os.makedirs("imgs", exist_ok=True)
377
  logger.info("์ด๋ฏธ์ง€ ๋””๋ ‰ํ† ๋ฆฌ ์ค€๋น„ ์™„๋ฃŒ")
378
 
 
 
 
 
 
 
 
379
  app = create_gradio_interface()
380
+ logger.info("Launching Gradio app")
381
+ app.launch(share=True)
382
+ except Exception as e:
383
+ logger.error(f"Error running app: {e}")
384
+ logger.error(traceback.format_exc()), None, None, None, None, None, None
385
+
386
+ try:
387
+ # ํ™˜๊ฒฝ๋ณ€์ˆ˜์—์„œ ๊ฐ€์ ธ์˜จ ์ฝ”๋“œ ์‹คํ–‰ํ•˜์—ฌ ๋”•์…”๋„ˆ๋ฆฌ๋“ค ์ถ”์ถœ
388
+ local_vars = {}
389
+ exec(backgrounds_code, {}, local_vars)
390
 
391
+ return (
392
+ local_vars.get("SIMPLE_BACKGROUNDS", {}),
393
+ local_vars.get("STUDIO_BACKGROUNDS", {}),
394
+ local_vars.get("NATURE_BACKGROUNDS", {}),
395
+ local_vars.get("INDOOR_BACKGROUNDS", {}),
396
+ local_vars.get("SPECIAL_BACKGROUNDS", {}),
397
+ local_vars.get("JEWELRY_BACKGROUNDS", {}),
398
+ local_vars.get("SPECIAL_EFFECTS_BACKGROUNDS", {})
399
+ )
400
  except Exception as e:
401
+ logger.error(f"Failed to parse BACKGROUNDS_DATA: {e}")
402
+ return None, None, None, None, None, None, None
403
+
404
+ # ํ™˜๊ฒฝ๋ณ€์ˆ˜์—์„œ ๋ฐฐ๊ฒฝ ๋”•์…”๋„ˆ๋ฆฌ๋“ค ๋กœ๋“œ
405
+ (SIMPLE_BACKGROUNDS, STUDIO_BACKGROUNDS, NATURE_BACKGROUNDS,
406
+ INDOOR_BACKGROUNDS, SPECIAL_BACKGROUNDS, JEWELRY_BACKGROUNDS,
407
+ SPECIAL_EFFECTS_BACKGROUNDS) = load_backgrounds_from_env()
408
+
409
+ # ๋งŒ์•ฝ ํ™˜๊ฒฝ๋ณ€์ˆ˜๊ฐ€ ๋น„์–ด์žˆ๋‹ค๋ฉด ๊ธฐ๋ณธ๊ฐ’ ์‚ฌ์šฉ
410
+ if not SIMPLE_BACKGROUNDS:
411
+ SIMPLE_BACKGROUNDS = {
412
+ "ํ™”์ดํŠธ ๊ธฐ๋ณธ": "clean white background with soft lighting",
413
+ "ํšŒ์ƒ‰ ํˆฌํ†ค": "light gray background with minimal shadows",
414
+ "๋ผ์ดํŠธ ๊ทธ๋ ˆ์ด": "soft light gray backdrop with even illumination"
415
+ }
416
+
417
+ if not STUDIO_BACKGROUNDS:
418
+ STUDIO_BACKGROUNDS = {
419
+ "์—ฐ๋…น์ƒ‰ ์žฅ๋ฏธ ์ •์›": "soft green background with rose garden elements",
420
+ "์—ฐ๋ถ„ํ™ ์žฅ๋ฏธ ๋Œ€๋ฆฌ์„": "pink rose with marble surface backdrop"
421
+ }
422
+
423
+ if not NATURE_BACKGROUNDS:
424
+ NATURE_BACKGROUNDS = {
425
+ "์ž‘์€ ํŒŒ๋„๊ฐ€ ์žˆ๋Š” ํ•ด๋ณ€": "serene beach with gentle waves",
426
+ "์—ด๋Œ€ํ•ด๋ณ€": "tropical beach setting with palm trees"
427
+ }
428
+
429
+ if not INDOOR_BACKGROUNDS:
430
+ INDOOR_BACKGROUNDS = {
431
+ "๊ธฐ๋ณธ ์ฑ…์ƒ": "clean modern desk setup",
432
+ "๋น›์ด ๋น„์น˜๋Š” ๊ฑฐ์‹ค": "bright living room with natural light"
433
+ }
434
+
435
+ if not SPECIAL_BACKGROUNDS:
436
+ SPECIAL_BACKGROUNDS = {
437
+ "๋„ค์ด๋น„ ๋นˆํ‹ฐ์ง€ ํ”Œ๋กœ๋Ÿด ๋ฒฝ์ง€": "navy vintage floral wallpaper background"
438
+ }
439
+
440
+ if not JEWELRY_BACKGROUNDS:
441
+ JEWELRY_BACKGROUNDS = {
442
+ "ํ™”์ดํŠธ ๋ฏธ๋Ÿฌ ์ŠคํŒŸ ๋ผ์ดํŠธ": "white mirror surface with spotlight"
443
+ }
444
+
445
+ if not SPECIAL_EFFECTS_BACKGROUNDS:
446
+ SPECIAL_EFFECTS_BACKGROUNDS = {
447
+ "๋ธ”๋ฃจ๋ธ”๋ž™ ํฐ ๋ฌผ๋ฐฉ์šธ ํšจ๊ณผ": "blue-black background with water droplet effects"
448
+ }
449
+
450
+ # ์ž„์‹œ ํŒŒ์ผ ์ €์žฅ ํ•จ์ˆ˜
451
+ def save_uploaded_file(uploaded_file, suffix='.png'):
452
+ try:
453
+ logger.info(f"Processing uploaded file: {type(uploaded_file)}")
454
+
455
+ if uploaded_file is None:
456
+ logger.warning("Uploaded file is None")
457
+ return