ginipick commited on
Commit
f7d50e3
·
verified ·
1 Parent(s): 4cba271

Delete app-BACKUP2.py

Browse files
Files changed (1) hide show
  1. app-BACKUP2.py +0 -613
app-BACKUP2.py DELETED
@@ -1,613 +0,0 @@
1
- import gradio as gr
2
- import replicate
3
- import requests
4
- import os
5
- import json
6
- import asyncio
7
- import concurrent.futures
8
- from io import BytesIO
9
- from PIL import Image
10
- from typing import List, Tuple, Dict
11
- import zipfile
12
- from datetime import datetime
13
- import time
14
- import traceback
15
-
16
- # 환경 변수에서 토큰 가져오기
17
- REPLICATE_API_TOKEN = os.getenv("RAPI_TOKEN")
18
- FRIENDLI_TOKEN = os.getenv("FRIENDLI_TOKEN")
19
-
20
- # 스타일 정의
21
- STYLE_TEMPLATES = {
22
- "3D Style (Pixar-like)": {
23
- "name": "3D Style",
24
- "description": "Pixar-esque 3D render with volumetric lighting",
25
- "use_case": "표지, 비전, 미래 컨셉",
26
- "example": "A fluffy ginger cat wearing a tiny spacesuit, floating amidst a vibrant nebula in a 3D render. The cat is gazing curiously at a swirling planet with rings made of candy. Background is filled with sparkling stars and colorful gas clouds, lit with soft, volumetric lighting. Style: Pixar-esque, highly detailed, playful. Colors: Deep blues, purples, oranges, and pinks. Rendered in Octane, 8k resolution."
27
- },
28
- "Elegant SWOT Quadrant": {
29
- "name": "SWOT Analysis",
30
- "description": "Flat-design 4-grid layout with minimal shadows",
31
- "use_case": "현황 분석, 전략 평가",
32
- "example": "Elegant SWOT quadrant: flat-design 4-grid on matte-white backdrop, thin pastel separators, top-left 'Strengths' panel shows glowing shield icon and subtle motif, top-right 'Weaknesses' panel with cracked chain icon in soft crimson, bottom-left 'Opportunities' panel with sunrise-over-horizon icon in optimistic teal, bottom-right 'Threats' panel with storm-cloud & lightning icon in deep indigo, minimal shadows, no text, no watermark, 16:9, 4K"
33
- },
34
- "Colorful Mind Map": {
35
- "name": "Mind Map",
36
- "description": "Hand-drawn educational style with vibrant colors",
37
- "use_case": "브레인스토밍, 아이디어 정리",
38
- "example": "A handrawn colorful mind map diagram: educational style, vibrant colors, clear hierarchy, golden ratio layout. Central concept with branching sub-topics, each branch with unique color coding, organic flowing connections, doodle-style icons for each node"
39
- },
40
- "Business Workflow": {
41
- "name": "Business Process",
42
- "description": "End-to-end business workflow with clear phases",
43
- "use_case": "프로세스 설명, 단계별 진행",
44
- "example": "A detailed hand-drawn diagram illustrating an end-to-end business workflow with Market Analysis, Strategy Development, Product Design, Implementation, and Post-Launch Review phases. Clear directional arrows, iconography for each component, vibrant educational yet professional style"
45
- },
46
- "Industrial Design": {
47
- "name": "Product Design",
48
- "description": "Sleek industrial design concept sketch",
49
- "use_case": "제품 소개, 컨셉 디자인",
50
- "example": "A sleek industrial design concept: Curved metallic body with minimal bezel, Touchscreen panel for settings, Modern matte black finish, Hand-drawn concept sketch style with annotations and dimension lines"
51
- },
52
- "3D Bubble Chart": {
53
- "name": "Bubble Chart",
54
- "description": "Clean 3D bubble visualization",
55
- "use_case": "비교 분석, 포지셔닝",
56
- "example": "3-D bubble chart on clean white 2×2 grid, quadrant titles hidden, four translucent spheres in lime, azure, amber, magenta, gentle depth-of-field, modern consulting aesthetic, no text, 4K"
57
- },
58
- "Timeline Ribbon": {
59
- "name": "Timeline",
60
- "description": "Horizontal ribbon timeline with cyber-futuristic vibe",
61
- "use_case": "일정, 로드맵, 마일스톤",
62
- "example": "Horizontal ribbon timeline, milestone pins glowing hot pink on charcoal, year markers as circles, faint motion streaks, cyber-futuristic vibe, no text, 1920×1080"
63
- },
64
- "Risk Heat Map": {
65
- "name": "Heat Map",
66
- "description": "Risk assessment heat map with gradient colors",
67
- "use_case": "리스크 분석, 우선순위",
68
- "example": "Risk Heat Map: square grid, smooth gradient from mint to fire-red, cells beveled, simple legend strip hidden, long subtle shadow, sterile white frame, no text"
69
- },
70
- "Pyramid/Funnel": {
71
- "name": "Funnel Chart",
72
- "description": "Multi-layer gradient funnel visualization",
73
- "use_case": "단계별 축소, 핵심 도출",
74
- "example": "Pyramid / Funnel: 5-layer gradient funnel narrowing downwards, top vivid sky-blue, mid mint-green, bottom sunset-orange, glass reflection, minimal background, no text"
75
- },
76
- "KPI Dashboard": {
77
- "name": "Dashboard",
78
- "description": "Dark-mode analytics dashboard with sci-fi interface",
79
- "use_case": "성과 지표, 실적 대시보드",
80
- "example": "KPI Dashboard: Dark-mode analytic dashboard, three glass speedometers glowing neon lime, two sparkline charts under, black glass background, sci-fi interface, no text, 4K"
81
- },
82
- "Value Chain": {
83
- "name": "Value Chain",
84
- "description": "Horizontal value chain with industrial look",
85
- "use_case": "가치 사슬, 비즈니스 모델",
86
- "example": "Value Chain Diagram: Horizontal value chain blocks, steel-blue gradient bars with subtle bevel, small gear icons above each segment, sleek industrial look, shadow cast, no text"
87
- },
88
- "Gantt Chart": {
89
- "name": "Gantt Chart",
90
- "description": "Hand-drawn style Gantt chart with playful colors",
91
- "use_case": "프로젝트 일정, 작업 관리",
92
- "example": "Gantt Chart: Hand-drawn style Gantt bars sketched with vibrant markers on dotted grid notebook page, sticky-note color palette, playful yet organized, perspective tilt, no text"
93
- },
94
- "Mobile App Mockup": {
95
- "name": "App Mockup",
96
- "description": "Clean wireframe for mobile app design",
97
- "use_case": "앱/웹 UI, 화면 설계",
98
- "example": "MOCKUP DESIGN: A clean hand-drawn style wireframe for a mobile app with Title screen, Login screen, Dashboard with sections, Bottom navigation bar, minimalist design with annotations"
99
- },
100
- "Flowchart": {
101
- "name": "Flowchart",
102
- "description": "Vibrant flowchart with minimalistic icons",
103
- "use_case": "의사결정, 프로세스 흐름",
104
- "example": "FLOWCHART DESIGN: A hand-drawn style flowchart, vibrant colors, minimalistic icons showing process flow from START to END with decision points, branches, and clear directional arrows"
105
- }
106
- }
107
-
108
- # PPT 템플릿 정의
109
- PPT_TEMPLATES = {
110
- "비즈니스 제안서": {
111
- "description": "투자 유치, 사업 제안용",
112
- "slides": [
113
- {"title": "표지", "style": "3D Style (Pixar-like)", "prompt_hint": "회사 비전과 미래"},
114
- {"title": "목차", "style": "Flowchart", "prompt_hint": "프레젠테이션 구조"},
115
- {"title": "문제 정의", "style": "Colorful Mind Map", "prompt_hint": "현재 시장의 문제점"},
116
- {"title": "현황 분석", "style": "Elegant SWOT Quadrant", "prompt_hint": "강점, 약점, 기회, 위협"},
117
- {"title": "솔루션", "style": "Industrial Design", "prompt_hint": "제품/서비스 컨셉"},
118
- {"title": "프로세스", "style": "Business Workflow", "prompt_hint": "실행 단계"},
119
- {"title": "일정", "style": "Timeline Ribbon", "prompt_hint": "주요 마일스톤"},
120
- {"title": "성과 예측", "style": "KPI Dashboard", "prompt_hint": "예상 성과 지표"},
121
- {"title": "투자 요청", "style": "Pyramid/Funnel", "prompt_hint": "투자 규모와 활용"}
122
- ]
123
- },
124
- "제품 소개": {
125
- "description": "신제품 런칭, 서비스 소개용",
126
- "slides": [
127
- {"title": "제품 컨셉", "style": "Industrial Design", "prompt_hint": "제품 디자인"},
128
- {"title": "사용자 니즈", "style": "Colorful Mind Map", "prompt_hint": "고객 페인포인트"},
129
- {"title": "기능 소개", "style": "Mobile App Mockup", "prompt_hint": "UI/UX 화면"},
130
- {"title": "작동 원리", "style": "Flowchart", "prompt_hint": "기능 플로우"},
131
- {"title": "시장 포지션", "style": "3D Bubble Chart", "prompt_hint": "경쟁사 비교"},
132
- {"title": "출시 일정", "style": "Timeline Ribbon", "prompt_hint": "런칭 로드맵"}
133
- ]
134
- },
135
- "프로젝트 보고": {
136
- "description": "진행 상황, 성과 보고용",
137
- "slides": [
138
- {"title": "프로젝트 개요", "style": "Business Workflow", "prompt_hint": "전체 프로세스"},
139
- {"title": "진행 현황", "style": "Gantt Chart", "prompt_hint": "작업 일정"},
140
- {"title": "리스크 관리", "style": "Risk Heat Map", "prompt_hint": "위험 요소"},
141
- {"title": "성과 지표", "style": "KPI Dashboard", "prompt_hint": "달성 실적"},
142
- {"title": "향후 계획", "style": "Timeline Ribbon", "prompt_hint": "다음 단계"}
143
- ]
144
- },
145
- "전략 기획": {
146
- "description": "중장기 전략, 비전 수립용",
147
- "slides": [
148
- {"title": "비전", "style": "3D Style (Pixar-like)", "prompt_hint": "미래 비전"},
149
- {"title": "환경 분석", "style": "Elegant SWOT Quadrant", "prompt_hint": "내외부 환경"},
150
- {"title": "전략 체계", "style": "Colorful Mind Map", "prompt_hint": "전략 구조"},
151
- {"title": "가치 사슬", "style": "Value Chain", "prompt_hint": "비즈니스 모델"},
152
- {"title": "실행 로드맵", "style": "Timeline Ribbon", "prompt_hint": "단계별 계획"},
153
- {"title": "목표 지표", "style": "KPI Dashboard", "prompt_hint": "KPI 목표"}
154
- ]
155
- },
156
- "사용자 정의": {
157
- "description": "직접 구성하기",
158
- "slides": []
159
- }
160
- }
161
-
162
- def generate_prompt_with_llm(topic: str, style_example: str = None, slide_context: str = None) -> str:
163
- """주제와 스타일 예제를 받아서 LLM을 사��해 이미지 프롬프트를 생성"""
164
- print(f"[LLM] 프롬프트 생성 시작: {slide_context}")
165
-
166
- url = "https://api.friendli.ai/dedicated/v1/chat/completions"
167
- headers = {
168
- "Authorization": f"Bearer {FRIENDLI_TOKEN}",
169
- "Content-Type": "application/json"
170
- }
171
-
172
- system_prompt = """You are an expert image prompt engineer specializing in creating prompts for professional presentation slides.
173
-
174
- Your task is to create prompts that:
175
- 1. Are highly specific and visual, perfect for PPT backgrounds or main visuals
176
- 2. Consider the slide's purpose and maintain consistency across a presentation
177
- 3. Include style references matching the given example
178
- 4. Focus on clean, professional visuals that won't distract from text overlays
179
- 5. Ensure high contrast areas for text readability when needed
180
- 6. Maintain brand consistency and professional aesthetics
181
-
182
- Important guidelines:
183
- - If given a style example, adapt the topic to match that specific visual style
184
- - Consider the slide context (e.g., "표지", "현황 분석") to create appropriate visuals
185
- - Always output ONLY the prompt without any explanation
186
- - Keep prompts between 50-150 words for optimal results
187
- - Ensure the visual supports rather than overwhelms the slide content"""
188
-
189
- user_message = f"Topic: {topic}"
190
- if style_example:
191
- user_message += f"\n\nStyle reference to follow:\n{style_example}"
192
- if slide_context:
193
- user_message += f"\n\nSlide context: {slide_context}"
194
-
195
- payload = {
196
- "model": "dep89a2fld32mcm",
197
- "messages": [
198
- {
199
- "role": "system",
200
- "content": system_prompt
201
- },
202
- {
203
- "role": "user",
204
- "content": user_message
205
- }
206
- ],
207
- "max_tokens": 300,
208
- "top_p": 0.8,
209
- "temperature": 0.7,
210
- "stream": False
211
- }
212
-
213
- try:
214
- response = requests.post(url, json=payload, headers=headers, timeout=30)
215
- if response.status_code == 200:
216
- result = response.json()
217
- prompt = result['choices'][0]['message']['content'].strip()
218
- print(f"[LLM] 프롬프트 생성 완료: {prompt[:50]}...")
219
- return prompt
220
- else:
221
- error_msg = f"프롬프트 생성 실패: {response.status_code}"
222
- print(f"[LLM] {error_msg}")
223
- return error_msg
224
- except Exception as e:
225
- error_msg = f"프롬프트 생성 중 오류 발생: {str(e)}"
226
- print(f"[LLM] {error_msg}")
227
- return error_msg
228
-
229
- def translate_to_english(text: str) -> str:
230
- """한글 텍스트를 영어로 번역 (LLM 사용)"""
231
- if not any(ord('가') <= ord(char) <= ord('힣') for char in text):
232
- return text
233
-
234
- print(f"[번역] 한글 감지, 영어로 번역 시작")
235
-
236
- url = "https://api.friendli.ai/dedicated/v1/chat/completions"
237
- headers = {
238
- "Authorization": f"Bearer {FRIENDLI_TOKEN}",
239
- "Content-Type": "application/json"
240
- }
241
-
242
- payload = {
243
- "model": "dep89a2fld32mcm",
244
- "messages": [
245
- {
246
- "role": "system",
247
- "content": "You are a translator. Translate the given Korean text to English. Only return the translation without any explanation."
248
- },
249
- {
250
- "role": "user",
251
- "content": text
252
- }
253
- ],
254
- "max_tokens": 500,
255
- "top_p": 0.8,
256
- "stream": False
257
- }
258
-
259
- try:
260
- response = requests.post(url, json=payload, headers=headers, timeout=30)
261
- if response.status_code == 200:
262
- result = response.json()
263
- translated = result['choices'][0]['message']['content'].strip()
264
- print(f"[번역] 완료")
265
- return translated
266
- else:
267
- print(f"[번역] 실패, 원본 사용")
268
- return text
269
- except Exception as e:
270
- print(f"[번역] 오류: {str(e)}, 원본 사용")
271
- return text
272
-
273
- def generate_image(prompt: str, seed: int = 10, slide_info: str = "") -> Tuple[Image.Image, str]:
274
- """Replicate API를 사용해 이미지 생성"""
275
- print(f"\n[이미지 생성] {slide_info}")
276
- print(f"[이미지 생성] 프롬프트: {prompt[:50]}...")
277
-
278
- try:
279
- english_prompt = translate_to_english(prompt)
280
-
281
- if not REPLICATE_API_TOKEN:
282
- error_msg = "RAPI_TOKEN 환경변수가 설정되지 않았습니다."
283
- print(f"[이미지 생성] 오류: {error_msg}")
284
- return None, error_msg
285
-
286
- print(f"[이미지 생성] Replicate API 호출 중...")
287
- client = replicate.Client(api_token=REPLICATE_API_TOKEN)
288
-
289
- input_params = {
290
- "seed": seed,
291
- "prompt": english_prompt,
292
- "speed_mode": "Extra Juiced 🚀 (even more speed)",
293
- "output_quality": 100
294
- }
295
-
296
- start_time = time.time()
297
- output = client.run(
298
- "prunaai/hidream-l1-fast:17c237d753218fed0ed477cb553902b6b75735f48c128537ab829096ef3d3645",
299
- input=input_params
300
- )
301
-
302
- elapsed = time.time() - start_time
303
- print(f"[이미지 생성] API 응답 받음 ({elapsed:.1f}초)")
304
-
305
- if output:
306
- if isinstance(output, str) and output.startswith('http'):
307
- print(f"[이미지 생성] URL에서 이미지 다운로드 중...")
308
- response = requests.get(output, timeout=30)
309
- img = Image.open(BytesIO(response.content))
310
- print(f"[이미지 생성] 완료!")
311
- return img, english_prompt
312
- else:
313
- print(f"[이미지 생성] 바이너리 데이터 처리 중...")
314
- img = Image.open(BytesIO(output.read()))
315
- print(f"[이미지 생성] 완료!")
316
- return img, english_prompt
317
- else:
318
- error_msg = "이미지 생성 실패 - 빈 응답"
319
- print(f"[이미지 생성] {error_msg}")
320
- return None, error_msg
321
-
322
- except Exception as e:
323
- error_msg = f"오류: {str(e)}"
324
- print(f"[이미지 생성] {error_msg}")
325
- print(f"[이미지 생성] 상세 오류:\n{traceback.format_exc()}")
326
- return None, error_msg
327
-
328
- def generate_ppt_images_sequential(topic: str, template_name: str, custom_slides: List[Dict], seed: int, progress=gr.Progress()):
329
- """PPT 템플릿에 따라 이미지를 순차적으로 생성하며 진행 상황 표시"""
330
- results = []
331
- images_for_gallery = []
332
-
333
- # 템플릿 선택
334
- if template_name == "사용자 정의" and custom_slides:
335
- slides = custom_slides
336
- else:
337
- slides = PPT_TEMPLATES[template_name]["slides"]
338
-
339
- if not slides:
340
- yield [], "슬라이드가 정의되지 않았습니다."
341
- return
342
-
343
- total_slides = len(slides)
344
- print(f"\n[PPT 생성] 시작 - 총 {total_slides}개 슬라이드")
345
- print(f"[PPT 생성] 주제: {topic}")
346
- print(f"[PPT 생성] 템플릿: {template_name}")
347
-
348
- # 각 슬라이드 순차 처리
349
- for i, slide in enumerate(slides):
350
- progress((i + 1) / (total_slides + 1), f"슬라이드 {i+1}/{total_slides} 처리 중...")
351
-
352
- slide_info = f"슬라이드 {i+1}: {slide['title']}"
353
- status_msg = f"\n### 🔄 {slide_info} 생성 중...\n"
354
-
355
- # 현재까지의 상태 업데이트
356
- yield images_for_gallery, status_msg + format_results_status(results)
357
-
358
- style_key = slide["style"]
359
- if style_key in STYLE_TEMPLATES:
360
- style_info = STYLE_TEMPLATES[style_key]
361
- slide_context = f"{slide['title']} - {slide.get('prompt_hint', '')}"
362
-
363
- # 프롬프트 생성
364
- prompt = generate_prompt_with_llm(topic, style_info["example"], slide_context)
365
-
366
- # 이미지 생성
367
- slide_seed = seed + i
368
- img, used_prompt = generate_image(prompt, slide_seed, slide_info)
369
-
370
- # 결과 저장
371
- result = {
372
- "slide_title": slide["title"],
373
- "style": slide["style"],
374
- "image": img,
375
- "prompt": prompt,
376
- "used_prompt": used_prompt,
377
- "success": img is not None
378
- }
379
- results.append(result)
380
-
381
- # 성공한 이미지는 갤러리에 추가
382
- if img is not None:
383
- caption = f"{i+1}. {slide['title']} ({style_info['name']})"
384
- images_for_gallery.append((img, caption))
385
-
386
- status_msg = f"\n### ✅ {slide_info} 완료!\n"
387
- else:
388
- status_msg = f"\n### ❌ {slide_info} 실패: {used_prompt}\n"
389
-
390
- # 즉시 업데이트
391
- yield images_for_gallery, status_msg + format_results_status(results)
392
-
393
- # 최종 결과
394
- progress(1.0, "완료!")
395
- successful = sum(1 for r in results if r["success"])
396
- final_status = f"\n## 🎉 생성 완료!\n총 {total_slides}개 슬라이드 중 {successful}개 성공\n\n"
397
- final_status += format_results_status(results)
398
-
399
- yield images_for_gallery, final_status
400
-
401
- def format_results_status(results: List[Dict]) -> str:
402
- """결과를 상태 메시지로 포맷"""
403
- if not results:
404
- return ""
405
-
406
- status_lines = ["### 📊 생성 결과:\n"]
407
- for i, result in enumerate(results):
408
- if result["success"]:
409
- status_lines.append(f"✅ **슬라이드 {i+1}: {result['slide_title']}**")
410
- status_lines.append(f" - 스타일: {result['style'].split('(')[0].strip()}")
411
- status_lines.append(f" - 프롬프트: {result['prompt'][:60]}...\n")
412
- else:
413
- status_lines.append(f"❌ **슬라이드 {i+1}: {result['slide_title']}** - 실패")
414
- status_lines.append(f" - 오류: {result['used_prompt']}\n")
415
-
416
- return "\n".join(status_lines)
417
-
418
- def create_custom_slides_ui():
419
- """사용자 정의 슬라이드 구성 UI"""
420
- slides = []
421
- for i in range(10):
422
- with gr.Row():
423
- with gr.Column(scale=2):
424
- title = gr.Textbox(
425
- label=f"슬라이드 {i+1} 제목",
426
- placeholder="예: 표지, 목차, 현황 분석...",
427
- visible=(i < 3)
428
- )
429
- with gr.Column(scale=3):
430
- style = gr.Dropdown(
431
- choices=list(STYLE_TEMPLATES.keys()),
432
- label=f"스타일 선택",
433
- visible=(i < 3)
434
- )
435
- with gr.Column(scale=3):
436
- hint = gr.Textbox(
437
- label=f"프롬프트 힌트",
438
- placeholder="이 슬라이드에서 표현하고 싶은 내용",
439
- visible=(i < 3)
440
- )
441
- slides.append({"title": title, "style": style, "hint": hint})
442
- return slides
443
-
444
- # Gradio 인터페이스 생성
445
- with gr.Blocks(title="PPT 이미지 생성기", theme=gr.themes.Soft()) as demo:
446
- gr.Markdown("""
447
- # 🎯 AI 기반 PPT 이미지 생성기
448
-
449
- ### 전문적인 프레젠테이션을 위한 맞춤형 이미지를 한 번에 생성하세요!
450
- """)
451
-
452
- # API 토큰 상태 확인
453
- if not REPLICATE_API_TOKEN:
454
- gr.Markdown("⚠️ **경고**: RAPI_TOKEN 환경 변수가 설정되지 않았습니다.")
455
- if not FRIENDLI_TOKEN:
456
- gr.Markdown("⚠️ **경고**: FRIENDLI_TOKEN 환경 변수가 설정되지 않았습니다.")
457
-
458
- with gr.Row():
459
- with gr.Column(scale=1):
460
- # 기본 입력
461
- topic_input = gr.Textbox(
462
- label="프레젠테이션 주제",
463
- placeholder="예: AI 스타트업 투자 유치, 신제품 런칭, 디지털 전환 전략",
464
- lines=2
465
- )
466
-
467
- # PPT 템플릿 선택
468
- template_select = gr.Dropdown(
469
- choices=list(PPT_TEMPLATES.keys()),
470
- label="PPT 템플릿 선택",
471
- value="비즈니스 제안서",
472
- info="목적에 맞는 템플릿을 선택하세요"
473
- )
474
-
475
- # 템플릿 설명
476
- template_info = gr.Markdown()
477
-
478
- # 시드 값
479
- seed_input = gr.Slider(
480
- minimum=1,
481
- maximum=100,
482
- value=10,
483
- step=1,
484
- label="시드 값"
485
- )
486
-
487
- generate_btn = gr.Button("🚀 PPT 이미지 세트 생성", variant="primary", size="lg")
488
-
489
- # 사용자 정의 섹션
490
- with gr.Accordion("📝 사용자 정의 슬라이드 구성", open=False) as custom_accordion:
491
- gr.Markdown("템플릿을 사용하지 않고 직접 슬라이드를 구성하세요.")
492
- custom_slides_components = create_custom_slides_ui()
493
-
494
- with gr.Column(scale=2):
495
- # 진행 상황 표시
496
- status_output = gr.Markdown(
497
- value="### 👆 템플릿을 선택하고 생성 버튼을 클릭하세요!\n\n생성 진행 상황이 실시간으로 표시됩니다."
498
- )
499
-
500
- # 결과 갤러리
501
- output_gallery = gr.Gallery(
502
- label="생성된 PPT 이미지",
503
- show_label=True,
504
- elem_id="gallery",
505
- columns=3,
506
- rows=3,
507
- object_fit="contain",
508
- height="auto"
509
- )
510
-
511
- # 스타일 가이드
512
- with gr.Accordion("📚 스타일별 활용 가이드", open=False):
513
- style_guide = "### PPT 제작을 위한 스타일 가이드\n\n"
514
- for style_name, style_info in STYLE_TEMPLATES.items():
515
- style_guide += f"**{style_name}**\n"
516
- style_guide += f"- 용도: {style_info['use_case']}\n"
517
- style_guide += f"- 특징: {style_info['description']}\n\n"
518
- gr.Markdown(style_guide)
519
-
520
- # 활용 팁
521
- gr.Markdown("""
522
- ---
523
- ### 💡 PPT 제작 팁:
524
-
525
- 1. **템플릿 활용**: 목적에 맞는 템플릿을 선택하면 최적화된 슬라이드 구성을 제공합니다
526
- 2. **일관성**: 하나의 주제로 전체 슬라이드를 생성하여 시각적 일관성을 유지합니다
527
- 3. **실시간 확인**: 각 슬라이드가 생성될 때마다 진행 상황을 확인할 수 있습니다
528
- 4. **고화질**: 모든 이미지는 프레젠테이션에 적합한 고해상도로 생성됩니다
529
- """)
530
-
531
- # 이벤트 핸들러
532
- def update_template_info(template_name):
533
- if template_name in PPT_TEMPLATES:
534
- template = PPT_TEMPLATES[template_name]
535
- info = f"**{template['description']}**\n\n포함된 슬라이드:\n"
536
- for i, slide in enumerate(template['slides']):
537
- info += f"{i+1}. {slide['title']} - {STYLE_TEMPLATES[slide['style']]['use_case']}\n"
538
- return info
539
- return ""
540
-
541
- def generate_ppt_images_handler(topic, template_name, seed, progress=gr.Progress(), *custom_inputs):
542
- if not topic.strip():
543
- yield [], "❌ 주제를 입력해주세요."
544
- return
545
-
546
- # 사용자 정의 슬라이드 처리
547
- custom_slides = []
548
- if template_name == "사용자 정의":
549
- for i in range(0, len(custom_inputs), 3):
550
- title = custom_inputs[i]
551
- style = custom_inputs[i+1] if i+1 < len(custom_inputs) else None
552
- hint = custom_inputs[i+2] if i+2 < len(custom_inputs) else ""
553
-
554
- if title and style:
555
- custom_slides.append({
556
- "title": title,
557
- "style": style,
558
- "prompt_hint": hint
559
- })
560
-
561
- # 순차적으로 이미지 생성하며 진행 상황 표시
562
- for gallery, status in generate_ppt_images_sequential(topic, template_name, custom_slides, seed, progress):
563
- yield gallery, status
564
-
565
- # 이벤트 연결
566
- template_select.change(
567
- fn=update_template_info,
568
- inputs=[template_select],
569
- outputs=[template_info]
570
- )
571
-
572
- # 사용자 정의 입력 수집
573
- all_custom_inputs = []
574
- for slide_components in custom_slides_components:
575
- all_custom_inputs.extend([
576
- slide_components["title"],
577
- slide_components["style"],
578
- slide_components["hint"]
579
- ])
580
-
581
- generate_btn.click(
582
- fn=generate_ppt_images_handler,
583
- inputs=[topic_input, template_select, seed_input] + all_custom_inputs,
584
- outputs=[output_gallery, status_output]
585
- )
586
-
587
- # 초기 템플릿 정보 표시
588
- demo.load(
589
- fn=update_template_info,
590
- inputs=[template_select],
591
- outputs=[template_info]
592
- )
593
-
594
- # 앱 실행
595
- if __name__ == "__main__":
596
- print("\n" + "="*50)
597
- print("🚀 PPT 이미지 생성기 시작!")
598
- print("="*50)
599
-
600
- # 환경 변수 확인
601
- if not REPLICATE_API_TOKEN:
602
- print("⚠️ 경고: RAPI_TOKEN 환경 변수가 설정되지 않았습니다.")
603
- else:
604
- print("✅ RAPI_TOKEN 확인됨")
605
-
606
- if not FRIENDLI_TOKEN:
607
- print("⚠️ 경고: FRIENDLI_TOKEN 환경 변수가 설정되지 않았습니다.")
608
- else:
609
- print("✅ FRIENDLI_TOKEN 확인됨")
610
-
611
- print("="*50 + "\n")
612
-
613
- demo.launch()