openfree commited on
Commit
82e4a81
ยท
verified ยท
1 Parent(s): d15f442

Delete app-backup1.py

Browse files
Files changed (1) hide show
  1. app-backup1.py +0 -1639
app-backup1.py DELETED
@@ -1,1639 +0,0 @@
1
- import os
2
- import random
3
- import base64
4
- import requests
5
- import tempfile
6
- import shutil
7
- import time
8
- import numpy as np
9
- import traceback
10
- from typing import List, Tuple
11
- from datetime import datetime, timedelta
12
- from pathlib import Path
13
- from io import BytesIO
14
- from urllib.parse import urljoin
15
-
16
- # Selenium ๊ด€๋ จ
17
- from selenium import webdriver
18
- from selenium.webdriver.support.ui import WebDriverWait
19
- from selenium.webdriver.support import expected_conditions as EC
20
- from selenium.webdriver.common.by import By
21
- from selenium.common.exceptions import WebDriverException, TimeoutException
22
-
23
- # ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ
24
- from PIL import Image, ImageDraw, ImageFont
25
-
26
- # Gradio
27
- import gradio as gr
28
-
29
- # HuggingFace
30
- from huggingface_hub import InferenceClient
31
- from dotenv import load_dotenv
32
-
33
- # HTML ํŒŒ์‹ฑ
34
- from bs4 import BeautifulSoup
35
-
36
- # ์Œ์„ฑ ๋ฐ ๋น„๋””์˜ค ์ฒ˜๋ฆฌ
37
- from gtts import gTTS
38
- from moviepy.editor import (
39
- VideoFileClip,
40
- AudioFileClip,
41
- ImageClip,
42
- concatenate_videoclips
43
- )
44
-
45
- # .env ํŒŒ์ผ์—์„œ ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ๋กœ๋“œ
46
- load_dotenv()
47
-
48
- # HuggingFace ์ธํผ๋Ÿฐ์Šค ํด๋ผ์ด์–ธํŠธ ์„ค์ •
49
- hf_client = InferenceClient(
50
- "CohereForAI/c4ai-command-r-plus-08-2024",
51
- token=os.getenv("HF_TOKEN")
52
- )
53
- # ์Šคํฌ๋ฆฐ์ƒท ์บ์‹œ ๋””๋ ‰ํ† ๋ฆฌ ์„ค์ •
54
- CACHE_DIR = Path("screenshot_cache")
55
- CACHE_DIR.mkdir(exist_ok=True)
56
-
57
- # ์ „์—ญ ๋ณ€์ˆ˜๋กœ ์Šคํฌ๋ฆฐ์ƒท ์บ์‹œ ์„ ์–ธ
58
- SCREENSHOT_CACHE = {}
59
-
60
- def get_cached_screenshot(url: str) -> str:
61
- """์บ์‹œ๋œ ์Šคํฌ๋ฆฐ์ƒท ๊ฐ€์ ธ์˜ค๊ธฐ ๋˜๋Š” ์ƒˆ๋กœ ์ƒ์„ฑ"""
62
- try:
63
- # URL์„ ์•ˆ์ „ํ•œ ํŒŒ์ผ๋ช…์œผ๋กœ ๋ณ€ํ™˜
64
- safe_filename = base64.urlsafe_b64encode(url.encode()).decode()
65
- cache_file = CACHE_DIR / f"{safe_filename[:200]}.jpg" # PNG ๋Œ€์‹  JPG ์‚ฌ์šฉ
66
-
67
- if cache_file.exists():
68
- try:
69
- with Image.open(cache_file) as img:
70
- buffered = BytesIO()
71
- img.save(buffered, format="JPEG", quality=85, optimize=True)
72
- return base64.b64encode(buffered.getvalue()).decode()
73
- except Exception as e:
74
- print(f"Cache read error for {url}: {e}")
75
- if cache_file.exists():
76
- cache_file.unlink()
77
-
78
- return take_screenshot(url)
79
-
80
- except Exception as e:
81
- print(f"Screenshot cache error for {url}: {e}")
82
- return ""
83
-
84
- def take_screenshot(url: str) -> str:
85
- """์›น์‚ฌ์ดํŠธ ์Šคํฌ๋ฆฐ์ƒท ์ดฌ์˜"""
86
- if not url.startswith('http'):
87
- url = f"https://{url}"
88
-
89
- options = webdriver.ChromeOptions()
90
- options.add_argument('--headless')
91
- options.add_argument('--no-sandbox')
92
- options.add_argument('--disable-dev-shm-usage')
93
- options.add_argument('--window-size=1080,720')
94
-
95
- driver = None
96
- try:
97
- driver = webdriver.Chrome(options=options)
98
- driver.get(url)
99
-
100
- # ํŽ˜์ด์ง€ ๋กœ๋”ฉ ๋Œ€๊ธฐ
101
- WebDriverWait(driver, 15).until(
102
- EC.presence_of_element_located((By.TAG_NAME, "body"))
103
- )
104
-
105
- # ์ถ”๊ฐ€ ๋Œ€๊ธฐ ์‹œ๊ฐ„
106
- time.sleep(3)
107
-
108
- # ์Šคํฌ๋ฆฐ์ƒท ์ดฌ์˜ ๋ฐ ์ตœ์ ํ™”
109
- screenshot = driver.get_screenshot_as_png()
110
- img = Image.open(BytesIO(screenshot))
111
-
112
- # ์ด๋ฏธ์ง€ ํฌ๊ธฐ ์ตœ์ ํ™”
113
- max_size = (800, 600)
114
- img.thumbnail(max_size, Image.Resampling.LANCZOS)
115
-
116
- # JPEG๋กœ ๋ณ€ํ™˜ ๋ฐ ์ตœ์ ํ™”
117
- if img.mode in ('RGBA', 'LA'):
118
- background = Image.new('RGB', img.size, (255, 255, 255))
119
- background.paste(img, mask=img.split()[-1])
120
- img = background
121
-
122
- # ์บ์‹œ ์ €์žฅ
123
- safe_filename = base64.urlsafe_b64encode(url.encode()).decode()
124
- cache_file = CACHE_DIR / f"{safe_filename[:200]}.jpg"
125
- img.save(cache_file, format="JPEG", quality=85, optimize=True)
126
-
127
- # ๋ฐ˜ํ™˜์šฉ ์ด๋ฏธ์ง€ ์ƒ์„ฑ
128
- buffered = BytesIO()
129
- img.save(buffered, format="JPEG", quality=85, optimize=True)
130
- return base64.b64encode(buffered.getvalue()).decode()
131
-
132
- except Exception as e:
133
- print(f"Screenshot error for {url}: {e}")
134
- return ""
135
-
136
- finally:
137
- if driver:
138
- driver.quit()
139
-
140
- def cleanup_cache():
141
- """์บ์‹œ ์ •๋ฆฌ"""
142
- try:
143
- current_time = time.time()
144
- for cache_file in CACHE_DIR.glob("*.jpg"):
145
- try:
146
- # 24์‹œ๊ฐ„ ์ด์ƒ ๋œ ํŒŒ์ผ ๋˜๋Š” 0๋ฐ”์ดํŠธ ํŒŒ์ผ ์‚ญ์ œ
147
- if (current_time - cache_file.stat().st_mtime > 86400) or cache_file.stat().st_size == 0:
148
- cache_file.unlink()
149
- except Exception as e:
150
- print(f"Error cleaning cache file {cache_file}: {e}")
151
-
152
- except Exception as e:
153
- print(f"Cache cleanup error: {e}")
154
-
155
- # ์•ฑ ์‹œ์ž‘ ์‹œ ์บ์‹œ ์ •๋ฆฌ
156
- cleanup_cache()
157
-
158
- def calculate_rising_rate(created_date: str, rank: int) -> int:
159
- """AI Rising Rate ๊ณ„์‚ฐ"""
160
- # ์ƒ์„ฑ์ผ ๊ธฐ์ค€ ์ ์ˆ˜ ๊ณ„์‚ฐ
161
- created = datetime.strptime(created_date.split('T')[0], '%Y-%m-%d')
162
- today = datetime.now()
163
- days_diff = (today - created).days
164
- date_score = max(0, 300 - days_diff) # ์ตœ๋Œ€ 300์ 
165
-
166
- # ์ˆœ์œ„ ๊ธฐ์ค€ ์ ์ˆ˜ ๊ณ„์‚ฐ
167
- rank_score = max(0, 600 - rank) # ์ตœ๋Œ€ 300์ 
168
-
169
- # ์ด์  ๊ณ„์‚ฐ
170
- total_score = date_score + rank_score
171
-
172
- # ๋ณ„ ๊ฐœ์ˆ˜ ๊ณ„์‚ฐ (0~5)
173
- if total_score <= 200:
174
- stars = 1
175
- elif total_score <= 400:
176
- stars = 2
177
- elif total_score <= 600:
178
- stars = 3
179
- elif total_score <= 800:
180
- stars = 4
181
- else:
182
- stars = 5
183
-
184
- return stars
185
-
186
- def get_popularity_grade(likes: int, stars: int) -> tuple:
187
- """AI Popularity Score ๋“ฑ๊ธ‰ ๊ณ„์‚ฐ"""
188
- # ๊ธฐ๋ณธ ์ ์ˆ˜ (likes)
189
- base_score = min(likes, 10000) # ์ตœ๋Œ€ 10000์ 
190
-
191
- # ๋ณ„์  ์ถ”๊ฐ€ ์ ์ˆ˜ (๋ณ„ ํ•˜๋‚˜๋‹น 500์ )
192
- star_score = stars * 1000
193
-
194
- # ์ด์ 
195
- total_score = base_score + star_score
196
-
197
- # ๋“ฑ๊ธ‰ ํ…Œ์ด๋ธ” (18๋‹จ๊ณ„)
198
- grades = [
199
- (14500, "AAA+"), (14000, "AAA"), (13500, "AAA-"),
200
- (13000, "AA+"), (12500, "AA"), (12000, "AA-"),
201
- (11500, "A+"), (11000, "A"), (10000, "A-"),
202
- (9000, "BBB+"), (8000, "BBB"), (7000, "BBB-"),
203
- (6000, "BB+"), (5000, "BB"), (4000, "BB-"),
204
- (3000, "B+"), (2000, "B"), (1000, "B-")
205
- ]
206
-
207
- for threshold, grade in grades:
208
- if total_score >= threshold:
209
- return grade, total_score
210
-
211
- return "B-", total_score
212
-
213
- # get_card ํ•จ์ˆ˜ ๋‚ด์˜ hardware_info ๋ถ€๋ถ„์„ ๋‹ค์Œ์œผ๋กœ ๊ต์ฒด:
214
- def get_rating_info(item: dict, index: int) -> str:
215
- """ํ‰๊ฐ€ ์ •๋ณด HTML ์ƒ์„ฑ"""
216
- created = item.get('createdAt', '').split('T')[0]
217
- likes = int(str(item.get('likes', '0')).replace(',', ''))
218
-
219
- # AI Rising Rate ๊ณ„์‚ฐ
220
- stars = calculate_rising_rate(created, index + 1)
221
- star_html = "โ˜…" * stars + "โ˜†" * (5 - stars) # ์ฑ„์›Œ์ง„ ๋ณ„๊ณผ ๋นˆ ๋ณ„ ์กฐํ•ฉ
222
-
223
- # AI Popularity Score ๊ณ„์‚ฐ
224
- grade, score = get_popularity_grade(likes, stars)
225
-
226
- # ๋“ฑ๊ธ‰๋ณ„ ์ƒ‰์ƒ ์„ค์ •
227
- grade_colors = {
228
- 'AAA': '#FFD700', 'AA': '#FFA500', 'A': '#FF4500',
229
- 'BBB': '#4169E1', 'BB': '#1E90FF', 'B': '#00BFFF'
230
- }
231
- grade_base = grade.rstrip('+-')
232
- grade_color = grade_colors.get(grade_base, '#666666')
233
-
234
- return f"""
235
- <div style='
236
- margin-top: 15px;
237
- padding: 15px;
238
- background: rgba(255,255,255,0.4);
239
- border-radius: 10px;
240
- font-size: 0.9em;
241
- box-shadow: 0 2px 10px rgba(0,0,0,0.1);'>
242
- <div style='
243
- display: grid;
244
- grid-template-columns: repeat(2, 1fr);
245
- gap: 15px;'>
246
- <div style='
247
- color: #333;
248
- display: flex;
249
- flex-direction: column;
250
- gap: 5px;'>
251
- <span style='font-weight: bold;'>AI Rising Rate:</span>
252
- <span style='
253
- color: #FF8C00;
254
- font-size: 1.4em;
255
- letter-spacing: 2px;
256
- text-shadow: 1px 1px 2px rgba(0,0,0,0.1);'>{star_html}</span>
257
- </div>
258
- <div style='
259
- color: #333;
260
- display: flex;
261
- flex-direction: column;
262
- gap: 5px;'>
263
- <span style='font-weight: bold;'>AI Popularity Score:</span>
264
- <span style='
265
- font-size: 1.2em;
266
- font-weight: bold;
267
- color: {grade_color};
268
- text-shadow: 1px 1px 2px rgba(0,0,0,0.1);'>{grade} ({score:,})</span>
269
- </div>
270
- </div>
271
- </div>
272
- """
273
-
274
- def get_hardware_info(item: dict) -> tuple:
275
- """ํ•˜๋“œ์›จ์–ด ์ •๋ณด ์ถ”์ถœ"""
276
- try:
277
- # runtime ์ •๋ณด ํ™•์ธ
278
- runtime = item.get('runtime', {})
279
-
280
- # CPU ์ •๋ณด ์ฒ˜๋ฆฌ
281
- cpu_info = runtime.get('cpu', 'Standard')
282
-
283
- # GPU ์ •๋ณด ์ฒ˜๋ฆฌ
284
- gpu_info = "None"
285
- if runtime.get('accelerator') == "gpu":
286
- gpu_type = runtime.get('gpu', {}).get('name', '')
287
- gpu_memory = runtime.get('gpu', {}).get('memory', '')
288
- if gpu_type:
289
- gpu_info = f"{gpu_type}"
290
- if gpu_memory:
291
- gpu_info += f" ({gpu_memory}GB)"
292
-
293
- # spaces decorator ํ™•์ธ
294
- if '@spaces.GPU' in str(item.get('sdk_version', '')):
295
- if gpu_info == "None":
296
- gpu_info = "GPU Enabled"
297
-
298
- # SDK ์ •๋ณด ์ฒ˜๋ฆฌ
299
- sdk = item.get('sdk', 'N/A')
300
-
301
- print(f"Debug - Runtime Info: {runtime}") # ๋””๋ฒ„๊ทธ ์ถœ๋ ฅ
302
- print(f"Debug - GPU Info: {gpu_info}") # ๋””๋ฒ„๊ทธ ์ถœ๋ ฅ
303
-
304
- return cpu_info, gpu_info, sdk
305
-
306
- except Exception as e:
307
- print(f"Error parsing hardware info: {str(e)}")
308
- return 'Standard', 'None', 'N/A'
309
-
310
- def get_card(item: dict, index: int, card_type: str = "space") -> str:
311
- """ํ†ตํ•ฉ ์นด๏ฟฝ๏ฟฝ๏ฟฝ HTML ์ƒ์„ฑ"""
312
- item_id = item.get('id', '')
313
- author, title = item_id.split('/', 1)
314
- likes = format(item.get('likes', 0), ',')
315
- created = item.get('createdAt', '').split('T')[0]
316
-
317
- # short_description ๊ฐ€์ ธ์˜ค๊ธฐ
318
- short_description = item.get('cardData', {}).get('short_description', '')
319
-
320
- # URL ์ •์˜
321
- if card_type == "space":
322
- url = f"https://huggingface.co/spaces/{item_id}"
323
- elif card_type == "model":
324
- url = f"https://huggingface.co/{item_id}"
325
- else: # dataset
326
- url = f"https://huggingface.co/datasets/{item_id}"
327
-
328
- # ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ
329
- tags = item.get('tags', [])
330
- pipeline_tag = item.get('pipeline_tag', '')
331
- license = item.get('license', '')
332
- sdk = item.get('sdk', 'N/A')
333
-
334
- # AI Rating ์ •๋ณด ๊ฐ€์ ธ์˜ค๊ธฐ
335
- rating_info = get_rating_info(item, index)
336
-
337
- # ์นด๋“œ ํƒ€์ž…๋ณ„ ๊ทธ๋ผ๋ฐ์ด์…˜ ์„ค์ •
338
- if card_type == "space":
339
- gradient_colors = """
340
- rgba(255, 182, 193, 0.7), /* ํŒŒ์Šคํ…” ํ•‘ํฌ */
341
- rgba(173, 216, 230, 0.7), /* ํŒŒ์Šคํ…” ๋ธ”๋ฃจ */
342
- rgba(255, 218, 185, 0.7) /* ํŒŒ์Šคํ…” ํ”ผ์น˜ */
343
- """
344
- bg_content = f"""
345
- background-image: url(data:image/png;base64,{get_cached_screenshot(url) if get_cached_screenshot(url) else ''});
346
- background-size: cover;
347
- background-position: center;
348
- """
349
- type_icon = "๐ŸŽฏ"
350
- type_label = "SPACE"
351
- elif card_type == "model":
352
- gradient_colors = """
353
- rgba(110, 142, 251, 0.7), /* ๋ชจ๋ธ ๋ธ”๋ฃจ */
354
- rgba(130, 158, 251, 0.7),
355
- rgba(150, 174, 251, 0.7)
356
- """
357
- bg_content = f"""
358
- background: linear-gradient(135deg, #6e8efb, #4a6cf7);
359
- padding: 15px;
360
- """
361
- type_icon = "๐Ÿค–"
362
- type_label = "MODEL"
363
- else: # dataset
364
- gradient_colors = """
365
- rgba(255, 107, 107, 0.7), /* ๋ฐ์ดํ„ฐ์…‹ ๋ ˆ๋“œ */
366
- rgba(255, 127, 127, 0.7),
367
- rgba(255, 147, 147, 0.7)
368
- """
369
- bg_content = f"""
370
- background: linear-gradient(135deg, #ff6b6b, #ff8787);
371
- padding: 15px;
372
- """
373
- type_icon = "๐Ÿ“Š"
374
- type_label = "DATASET"
375
-
376
- content_bg = f"""
377
- background: linear-gradient(135deg, {gradient_colors});
378
- backdrop-filter: blur(10px);
379
- """
380
-
381
- # ํƒœ๊ทธ ํ‘œ์‹œ (models์™€ datasets์šฉ)
382
- tags_html = ""
383
- if card_type != "space":
384
- tags_html = f"""
385
- <div style='
386
- position: absolute;
387
- top: 50%;
388
- left: 50%;
389
- transform: translate(-50%, -50%);
390
- display: flex;
391
- flex-wrap: wrap;
392
- gap: 5px;
393
- justify-content: center;
394
- width: 90%;'>
395
- {' '.join([f'''
396
- <span style='
397
- background: rgba(255,255,255,0.2);
398
- padding: 5px 10px;
399
- border-radius: 15px;
400
- color: white;
401
- font-size: 0.8em;'>
402
- #{tag}
403
- </span>
404
- ''' for tag in tags[:5]])}
405
- </div>
406
- """
407
-
408
- # ์นด๋“œ HTML ๋ฐ˜ํ™˜
409
- return f"""
410
- <div class="card" style='
411
- position: relative;
412
- border: none;
413
- padding: 0;
414
- margin: 10px;
415
- border-radius: 20px;
416
- box-shadow: 0 10px 20px rgba(0,0,0,0.1);
417
- background: white;
418
- transition: all 0.3s ease;
419
- overflow: hidden;
420
- min-height: 400px;
421
- cursor: pointer;
422
- transform-origin: center;'
423
- onmouseover="this.style.transform='scale(0.98) translateY(5px)'; this.style.boxShadow='0 5px 15px rgba(0,0,0,0.2)';"
424
- onmouseout="this.style.transform='scale(1) translateY(0)'; this.style.boxShadow='0 10px 20px rgba(0,0,0,0.1)';"
425
- onclick="window.open('{url}', '_blank')">
426
-
427
- <!-- ์ƒ๋‹จ ์˜์—ญ -->
428
- <div style='
429
- width: 100%;
430
- height: 200px;
431
- {bg_content}
432
- position: relative;'>
433
-
434
- <!-- ์ˆœ์œ„ ๋ฑƒ์ง€ -->
435
- <div style='
436
- position: absolute;
437
- top: 10px;
438
- left: 10px;
439
- background: rgba(0,0,0,0.7);
440
- color: white;
441
- padding: 5px 15px;
442
- border-radius: 20px;
443
- font-weight: bold;
444
- font-size: 0.9em;
445
- backdrop-filter: blur(5px);'>
446
- #{index + 1}
447
- </div>
448
-
449
- <!-- ํƒ€์ž… ๋ฑƒ์ง€ -->
450
- <div style='
451
- position: absolute;
452
- top: 10px;
453
- right: 10px;
454
- background: rgba(255,255,255,0.9);
455
- padding: 5px 15px;
456
- border-radius: 20px;
457
- font-weight: bold;
458
- font-size: 0.8em;'>
459
- {type_icon} {type_label}
460
- </div>
461
-
462
- {tags_html}
463
- </div>
464
-
465
- <!-- ์ฝ˜ํ…์ธ  ์˜์—ญ -->
466
- <div style='
467
- padding: 20px;
468
- {content_bg}
469
- border-radius: 0 0 20px 20px;
470
- border-top: 1px solid rgba(255,255,255,0.5);'>
471
- <h3 style='
472
- margin: 0 0 15px 0;
473
- color: #333;
474
- font-size: 1.3em;
475
- line-height: 1.4;
476
- display: -webkit-box;
477
- -webkit-line-clamp: 2;
478
- -webkit-box-orient: vertical;
479
- overflow: hidden;
480
- text-overflow: ellipsis;
481
- text-shadow: 1px 1px 1px rgba(255,255,255,0.8);'>
482
- {title}
483
- </h3>
484
-
485
- {f'''
486
- <!-- Short Description (Space ์นด๋“œ์—๋งŒ ํ‘œ์‹œ) -->
487
- <div style='
488
- margin: 0 0 15px 0;
489
- color: #444;
490
- font-size: 0.9em;
491
- line-height: 1.5;
492
- display: -webkit-box;
493
- -webkit-line-clamp: 3;
494
- -webkit-box-orient: vertical;
495
- overflow: hidden;
496
- text-overflow: ellipsis;
497
- background: rgba(255,255,255,0.4);
498
- padding: 10px;
499
- border-radius: 8px;'>
500
- {short_description}
501
- </div>
502
- ''' if card_type == "space" and short_description else ''}
503
-
504
- <div style='
505
- display: grid;
506
- grid-template-columns: repeat(2, 1fr);
507
- gap: 10px;
508
- font-size: 0.9em;
509
- background: rgba(255,255,255,0.3);
510
- padding: 10px;
511
- border-radius: 10px;'>
512
- <div style='color: #444;'>
513
- <span style='margin-right: 5px;'>๐Ÿ‘ค</span> {author}
514
- </div>
515
- <div style='color: #444;'>
516
- <span style='margin-right: 5px;'>โค๏ธ</span> {likes}
517
- </div>
518
- <div style='color: #444; grid-column: span 2;'>
519
- <span style='margin-right: 5px;'>๐Ÿ“…</span> {created}
520
- </div>
521
- </div>
522
-
523
- {rating_info}
524
- </div>
525
- </div>
526
- """
527
-
528
- def get_trending_spaces(search_query="", sort_by="rank", progress=gr.Progress()) -> Tuple[str, str]:
529
- """ํŠธ๋ Œ๋”ฉ ์ŠคํŽ˜์ด์Šค ๊ฐ€์ ธ์˜ค๊ธฐ"""
530
- url = "https://huggingface.co/api/spaces"
531
-
532
- try:
533
- progress(0, desc="Fetching spaces data...")
534
- params = {
535
- 'full': 'true',
536
- 'limit': 24
537
- }
538
-
539
- response = requests.get(url, params=params)
540
- response.raise_for_status()
541
- spaces = response.json()
542
-
543
- # ๊ฒ€์ƒ‰์–ด๋กœ ํ•„ํ„ฐ๋ง
544
- if search_query:
545
- spaces = [space for space in spaces if search_query.lower() in
546
- (space.get('id', '') + ' ' + space.get('title', '')).lower()]
547
-
548
- # ์ •๋ ฌ
549
- sort_by = sort_by.lower()
550
- if sort_by == "rising_rate":
551
- spaces.sort(key=lambda x: calculate_rising_rate(x.get('createdAt', ''), 0), reverse=True)
552
- elif sort_by == "popularity":
553
- spaces.sort(key=lambda x: get_popularity_grade(
554
- int(str(x.get('likes', '0')).replace(',', '')),
555
- calculate_rising_rate(x.get('createdAt', ''), 0))[1],
556
- reverse=True)
557
-
558
- progress(0.1, desc="Creating gallery...")
559
- html_content = """
560
- <div style='padding: 20px; background: #f5f5f5;'>
561
- <div style='display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 20px;'>
562
- """
563
-
564
- for idx, space in enumerate(spaces):
565
- html_content += get_card(space, idx, "space")
566
- progress((0.1 + 0.9 * idx/len(spaces)), desc=f"Loading space {idx+1}/{len(spaces)}...")
567
-
568
- html_content += "</div></div>"
569
-
570
- progress(1.0, desc="Complete!")
571
- return html_content, f"Found {len(spaces)} spaces"
572
-
573
- except Exception as e:
574
- error_html = f'<div style="color: red; padding: 20px;">Error: {str(e)}</div>'
575
- return error_html, f"Error: {str(e)}"
576
-
577
- def get_models(search_query="", sort_by="rank", progress=gr.Progress()) -> Tuple[str, str]:
578
- """์ธ๊ธฐ ๋ชจ๋ธ ๊ฐ€์ ธ์˜ค๊ธฐ"""
579
- url = "https://huggingface.co/api/models"
580
-
581
- try:
582
- progress(0, desc="Fetching models data...")
583
- params = {
584
- 'full': 'true',
585
- 'limit': 300
586
- }
587
- response = requests.get(url, params=params)
588
- response.raise_for_status()
589
- models = response.json()
590
-
591
- # ๊ฒ€์ƒ‰์–ด๋กœ ํ•„ํ„ฐ๋ง
592
- if search_query:
593
- models = [model for model in models if search_query.lower() in
594
- (model.get('id', '') + ' ' + model.get('title', '')).lower()]
595
-
596
- # ์ •๋ ฌ
597
- sort_by = sort_by.lower()
598
- if sort_by == "rising_rate":
599
- models.sort(key=lambda x: calculate_rising_rate(x.get('createdAt', ''), 0), reverse=True)
600
- elif sort_by == "popularity":
601
- models.sort(key=lambda x: get_popularity_grade(
602
- int(str(x.get('likes', '0')).replace(',', '')),
603
- calculate_rising_rate(x.get('createdAt', ''), 0))[1],
604
- reverse=True)
605
-
606
- progress(0.1, desc="Creating gallery...")
607
- html_content = """
608
- <div style='padding: 20px; background: #f5f5f5;'>
609
- <div style='display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 20px;'>
610
- """
611
-
612
- for idx, model in enumerate(models):
613
- html_content += get_card(model, idx, "model")
614
- progress((0.1 + 0.9 * idx/len(models)), desc=f"Loading model {idx+1}/{len(models)}...")
615
-
616
- html_content += "</div></div>"
617
-
618
- progress(1.0, desc="Complete!")
619
- return html_content, f"Found {len(models)} models"
620
-
621
- except Exception as e:
622
- error_html = f'<div style="color: red; padding: 20px;">Error: {str(e)}</div>'
623
- return error_html, f"Error: {str(e)}"
624
-
625
- def get_datasets(search_query="", sort_by="rank", progress=gr.Progress()) -> Tuple[str, str]:
626
- """์ธ๊ธฐ ๋ฐ์ดํ„ฐ์…‹ ๊ฐ€์ ธ์˜ค๊ธฐ"""
627
- url = "https://huggingface.co/api/datasets"
628
-
629
- try:
630
- progress(0, desc="Fetching datasets data...")
631
- params = {
632
- 'full': 'true',
633
- 'limit': 300
634
- }
635
- response = requests.get(url, params=params)
636
- response.raise_for_status()
637
- datasets = response.json()
638
-
639
- # ๊ฒ€์ƒ‰์–ด๋กœ ํ•„ํ„ฐ๋ง
640
- if search_query:
641
- datasets = [dataset for dataset in datasets if search_query.lower() in
642
- (dataset.get('id', '') + ' ' + dataset.get('title', '')).lower()]
643
-
644
- # ์ •๋ ฌ
645
- sort_by = sort_by.lower()
646
- if sort_by == "rising_rate":
647
- datasets.sort(key=lambda x: calculate_rising_rate(x.get('createdAt', ''), 0), reverse=True)
648
- elif sort_by == "popularity":
649
- datasets.sort(key=lambda x: get_popularity_grade(
650
- int(str(x.get('likes', '0')).replace(',', '')),
651
- calculate_rising_rate(x.get('createdAt', ''), 0))[1],
652
- reverse=True)
653
-
654
- progress(0.1, desc="Creating gallery...")
655
- html_content = """
656
- <div style='padding: 20px; background: #f5f5f5;'>
657
- <div style='display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 20px;'>
658
- """
659
-
660
- for idx, dataset in enumerate(datasets):
661
- html_content += get_card(dataset, idx, "dataset")
662
- progress((0.1 + 0.9 * idx/len(datasets)), desc=f"Loading dataset {idx+1}/{len(datasets)}...")
663
-
664
- html_content += "</div></div>"
665
-
666
- progress(1.0, desc="Complete!")
667
- return html_content, f"Found {len(datasets)} datasets"
668
-
669
- except Exception as e:
670
- error_html = f'<div style="color: red; padding: 20px;">Error: {str(e)}</div>'
671
- return error_html, f"Error: {str(e)}"
672
-
673
- # ์ •๋ ฌ ํ•จ์ˆ˜ ์ถ”๊ฐ€
674
- def sort_items(items, sort_by):
675
- if sort_by == "rank":
676
- return items # ์ด๋ฏธ ์ˆœ์œ„๋Œ€๋กœ ์ •๋ ฌ๋˜์–ด ์žˆ์Œ
677
- elif sort_by == "rising_rate":
678
- return sorted(items, key=lambda x: calculate_rising_rate(x.get('createdAt', ''), 0), reverse=True)
679
- elif sort_by == "popularity":
680
- return sorted(items, key=lambda x: get_popularity_grade(int(str(x.get('likes', '0')).replace(',', '')),
681
- calculate_rising_rate(x.get('createdAt', ''), 0))[1], reverse=True)
682
- return items
683
-
684
- # API ํ˜ธ์ถœ ํ•จ์ˆ˜ ์ˆ˜์ •
685
- def fetch_items(item_type, search_query="", sort_by="rank", limit=1000):
686
- """์•„์ดํ…œ ๊ฐ€์ ธ์˜ค๊ธฐ (spaces/models/datasets)"""
687
- base_url = f"https://huggingface.co/api/{item_type}"
688
- params = {
689
- 'full': 'true',
690
- 'limit': limit,
691
- 'search': search_query
692
- }
693
-
694
- try:
695
- response = requests.get(base_url, params=params)
696
- response.raise_for_status()
697
- items = response.json()
698
-
699
- # ๊ฒ€์ƒ‰์–ด๋กœ ํ•„ํ„ฐ๋ง
700
- if search_query:
701
- items = [item for item in items if search_query.lower() in
702
- (item.get('id', '') + item.get('title', '')).lower()]
703
-
704
- # ์ •๋ ฌ
705
- items = sort_items(items, sort_by)
706
-
707
- return items[:300] # ์ƒ์œ„ 300๊ฐœ๋งŒ ๋ฐ˜ํ™˜
708
- except Exception as e:
709
- print(f"Error fetching items: {e}")
710
- return []
711
-
712
-
713
-
714
- def get_space_source(space_id: str) -> dict:
715
- """์ŠคํŽ˜์ด์Šค์˜ ์†Œ์Šค์ฝ”๋“œ ๊ฐ€์ ธ์˜ค๊ธฐ"""
716
- try:
717
- # HuggingFace API๋ฅผ ํ†ตํ•ด ์†Œ์Šค์ฝ”๋“œ ๊ฐ€์ ธ์˜ค๊ธฐ
718
- headers = {"Authorization": f"Bearer {os.getenv('HF_TOKEN')}"}
719
-
720
- # app.py ์‹œ๋„
721
- app_url = f"https://huggingface.co/spaces/{space_id}/raw/main/app.py"
722
- app_response = requests.get(app_url, headers=headers)
723
-
724
- # index.html ์‹œ๋„
725
- index_url = f"https://huggingface.co/spaces/{space_id}/raw/main/index.html"
726
- index_response = requests.get(index_url, headers=headers)
727
-
728
- source = {
729
- "app.py": app_response.text if app_response.status_code == 200 else "",
730
- "index.html": index_response.text if index_response.status_code == 200 else ""
731
- }
732
-
733
- return source
734
- except Exception as e:
735
- print(f"Error fetching source for {space_id}: {str(e)}")
736
- return {"app.py": "", "index.html": ""}
737
-
738
- def analyze_space(space_info: dict) -> str:
739
- """์ŠคํŽ˜์ด์Šค ๋ถ„์„"""
740
- try:
741
- space_id = space_info.get('id', '')
742
- url = f"https://huggingface.co/spaces/{space_id}"
743
-
744
- # ์†Œ์Šค์ฝ”๋“œ ๊ฐ€์ ธ์˜ค๊ธฐ
745
- source = get_space_source(space_id)
746
- source_code = source["app.py"] or source["index.html"]
747
-
748
- if not source_code:
749
- return f"""
750
- <div style='
751
- padding: 20px;
752
- color: #333 !important;
753
- background: white !important;
754
- '>
755
- <h3 style='color: #333 !important; margin-bottom: 10px;'>
756
- #{space_info.get('rank', '0')} {space_id}
757
- </h3>
758
- <p style='color: red;'>์†Œ์Šค์ฝ”๋“œ๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.</p>
759
- </div>
760
- """
761
-
762
- # LLM ๋ถ„์„ ํ”„๋กฌํ”„ํŠธ
763
- prompt = f"""
764
- ๋‹ค์Œ์€ HuggingFace ์ŠคํŽ˜์ด์Šค({url})์˜ ์ฝ”๋“œ ๋˜๋Š” ์ฃผ์„์ž…๋‹ˆ๋‹ค:
765
-
766
- ```
767
- {source_code[:4000]}
768
- ```
769
-
770
- ์ด ๋‚ด์šฉ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๋‹ค์Œ ํ•ญ๋ชฉ์„ ๋ถ„์„ํ•ด์ฃผ์„ธ์š”:
771
- 1. ๊ฐœ์š”: (ํ•œ ์ค„๋กœ)
772
- 2. ์š”์•ฝ: (ํ•œ ์ค„๋กœ)
773
- 3. ํŠน์ง• ๋ฐ ์žฅ์ : (ํ•œ ์ค„๋กœ)
774
- 4. ์‚ฌ์šฉ ๋Œ€์ƒ: (ํ•œ ์ค„๋กœ)
775
- 5. ์‚ฌ์šฉ ๋ฐฉ๋ฒ•: (ํ•œ ์ค„๋กœ)
776
- 6. ์œ ์‚ฌ ์„œ๋น„์Šค์™€์˜ ์ฐจ๋ณ„์ : (ํ•œ ์ค„๋กœ)
777
-
778
- ๊ฐ ํ•ญ๋ชฉ์€ ์‹ค์ œ ํ™•์ธ๋œ ๋‚ด์šฉ๋งŒ ํฌํ•จํ•˜์—ฌ ํ•œ ์ค„๋กœ ์ž‘์„ฑํ•˜์„ธ์š”.
779
- ์ฝ”๋“œ๊ฐ€ ๋ณด์•ˆ์ฒ˜๋ฆฌ๋œ ๊ฒฝ์šฐ ์ฃผ์„์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๋ถ„์„ํ•˜์„ธ์š”.
780
- """
781
-
782
- # LLM ๋ถ„์„ ์‹คํ–‰
783
- messages = [
784
- {"role": "system", "content": "์†Œ์Šค์ฝ”๋“œ ๋ถ„์„ ์ „๋ฌธ๊ฐ€๋กœ์„œ ์‹ค์ œ ์ฝ”๋“œ ๋‚ด์šฉ๋งŒ ๊ธฐ๋ฐ˜์œผ๋กœ ๋ถ„์„ํ•˜์„ธ์š”."},
785
- {"role": "user", "content": prompt}
786
- ]
787
-
788
- response = hf_client.chat_completion(
789
- messages,
790
- max_tokens=3900,
791
- temperature=0.3
792
- )
793
-
794
- analysis = response.choices[0].message.content
795
-
796
- return f"""
797
- <div style='
798
- padding: 20px;
799
- color: #333 !important;
800
- background: white !important;
801
- '>
802
- <h3 style='color: #333 !important; margin-bottom: 15px;'>
803
- #{space_info.get('rank', '0')} {space_id}
804
- </h3>
805
- <div style='color: #444 !important; font-size: 0.95em;'>
806
- {analysis}
807
- </div>
808
- <div style='margin-top: 15px;'>
809
- <details>
810
- <summary style='cursor: pointer; color: #666 !important;'>
811
- ์†Œ์Šค์ฝ”๋“œ ๋ฏธ๋ฆฌ๋ณด๊ธฐ
812
- </summary>
813
- <pre style='
814
- background: #f5f5f5 !important;
815
- padding: 10px;
816
- border-radius: 5px;
817
- font-size: 0.8em;
818
- margin-top: 10px;
819
- white-space: pre-wrap;
820
- word-break: break-all;
821
- color: #333 !important;
822
- '>{source_code[:500]}...</pre>
823
- </details>
824
- </div>
825
- </div>
826
- """
827
-
828
- except Exception as e:
829
- return f"<div style='color: red !important; padding: 20px;'>๋ถ„์„ ์˜ค๋ฅ˜: {str(e)}</div>"
830
-
831
-
832
- def analyze_top_spaces(progress=gr.Progress()) -> Tuple[str, str]:
833
- """์ƒ์œ„ 24๊ฐœ ์ŠคํŽ˜์ด์Šค ๋ถ„์„"""
834
- try:
835
- progress(0, desc="์ŠคํŽ˜์ด์Šค ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๋Š” ์ค‘...")
836
-
837
- url = "https://huggingface.co/api/spaces"
838
- response = requests.get(url, params={'full': 'true', 'limit': 24})
839
- response.raise_for_status()
840
- spaces = response.json()[:24]
841
-
842
- # ์ƒ๋‹จ ์ž…๋ ฅ ๋ฐ•์Šค์™€ ๊ธฐ๋ณธ ํ…์ŠคํŠธ๋ฅผ ํฌํ•จํ•œ HTML ์‹œ์ž‘
843
- html_content = """
844
- <div style='padding: 20px; background: #ffffff;'>
845
- <div style='margin-bottom: 30px;'>
846
- <textarea id='intro_text' rows='4' style='
847
- width: 100%;
848
- padding: 15px;
849
- border: 1px solid #ddd;
850
- border-radius: 10px;
851
- font-size: 1.1em;
852
- line-height: 1.5;
853
- resize: vertical;
854
- background: #f8f9fa;
855
- '>์•ˆ๋…•ํ•˜์„ธ์š”. ๋งค์ผ ๊ธ€๋กœ๋ฒŒ ์ตœ์‹  AI ์ธ๊ธฐ ํŠธ๋ Œ๋“œ ์„œ๋น„์Šค๋ฅผ ์•Œ์•„๋ณด๋Š” '๋ฐ์ผ๋ฆฌ AI ํŠธ๋ Œ๋”ฉ' ๋‰ด์Šค์ž…๋‹ˆ๋‹ค. ์˜ค๋Š˜์˜ ํ—ˆ๊น…ํŽ˜์ด์Šค ์ธ๊ธฐ ์ˆœ์œ„ 1์œ„๋ถ€ํ„ฐ 24์œ„๊นŒ์ง€, ๋ถ„์„๊ณผ ํ•ต์‹ฌ ๋‚ด์šฉ์„ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.</textarea>
856
- </div>
857
- <style>
858
- .script-card {
859
- background: white !important;
860
- border-radius: 10px;
861
- padding: 20px;
862
- margin-bottom: 20px;
863
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
864
- border: 1px solid #e0e0e0;
865
- }
866
- .script-content {
867
- color: #444 !important;
868
- font-size: 1.1em;
869
- line-height: 1.6;
870
- white-space: pre-line;
871
- }
872
- </style>
873
- """
874
-
875
- for idx, space in enumerate(spaces):
876
- progress((idx + 1) / 24, desc=f"๋ถ„์„ ์ค‘... {idx+1}/24")
877
-
878
- try:
879
- source = get_space_source(space['id'])
880
- source_code = source["app.py"] or source["index.html"]
881
-
882
- # ์ŠคํŽ˜์ด์Šค ID์—์„œ ์‚ฌ์šฉ์ž๋ช… ์ œ๊ฑฐํ•˜๊ณ  ํ”„๋กœ์ ํŠธ๋ช…๋งŒ ์ถ”์ถœ
883
- project_name = space['id'].split('/')[-1]
884
-
885
- prompt = f"""
886
- ๋‹ค์Œ HuggingFace ์ŠคํŽ˜์ด์Šค๋ฅผ ์œ ํŠœ๋ธŒ ๋‰ด์Šค ๋ฆฌํฌํŠธ ํ˜•์‹์œผ๋กœ ์„ค๋ช…ํ•ด์ฃผ์„ธ์š”.
887
- ์‹œ์ž‘์€ ๋ฐ˜๋“œ์‹œ "์˜ค๋Š˜์˜ ์ธ๊ธฐ์ˆœ์œ„ {idx + 1}์œ„์ธ {project_name}์ž…๋‹ˆ๋‹ค."๋กœ ์‹œ์ž‘ํ•˜๊ณ ,
888
- ์ด์–ด์„œ ์ฃผ์š” ๊ธฐ๋Šฅ, ํŠน์ง•, ํ™œ์šฉ๋ฐฉ์•ˆ์„ 2-3๋ฌธ์žฅ์œผ๋กœ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์„ค๋ช…ํ•ด์ฃผ์„ธ์š”.
889
- ์ „์ฒด ๊ธธ์ด๋Š” 3-4๋ฌธ์žฅ์œผ๋กœ ์ œํ•œํ•˜๊ณ , ์„ค๋ช…์€ ๋‰ด์Šค ๋ฆฌํฌํ„ฐ์ฒ˜๋Ÿผ ๋ช…ํ™•ํ•˜๊ณ  ์ „๋ฌธ์ ์œผ๋กœ ํ•ด์ฃผ์„ธ์š”.
890
-
891
- ์†Œ์Šค์ฝ”๋“œ:
892
- ```
893
- {source_code[:1500]}
894
- ```
895
- """
896
-
897
- messages = [
898
- {"role": "system", "content": "AI ๊ธฐ์ˆ  ์ „๋ฌธ ๋‰ด์Šค ๋ฆฌํฌํ„ฐ์ž…๋‹ˆ๋‹ค."},
899
- {"role": "user", "content": prompt}
900
- ]
901
-
902
- response = hf_client.chat_completion(
903
- messages,
904
- max_tokens=2000,
905
- temperature=0.7
906
- )
907
-
908
- script = response.choices[0].message.content.strip()
909
-
910
- html_content += f"""
911
- <div class='script-card'>
912
- <div class='script-content'>{script}</div>
913
- </div>
914
- """
915
-
916
- except Exception as e:
917
- print(f"Error analyzing space {space['id']}: {e}")
918
- html_content += f"""
919
- <div class='script-card'>
920
- <div class='script-content' style='color: red !important;'>
921
- ์ˆœ์œ„ {idx + 1}์œ„ ๋ถ„์„ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.
922
- </div>
923
- </div>
924
- """
925
-
926
- html_content += "</div>"
927
- return html_content, f"24๊ฐœ ์ŠคํŽ˜์ด์Šค ๋ถ„์„ ์™„๋ฃŒ"
928
-
929
- except Exception as e:
930
- error_msg = f"Error: {str(e)}"
931
- return f"<div style='color: red; padding: 20px;'>{error_msg}</div>", error_msg
932
-
933
-
934
- def analyze_single_space(space: dict, source_code: str) -> str:
935
- """๋‹จ์ผ ์ŠคํŽ˜์ด์Šค ๋ถ„์„"""
936
- try:
937
- if not source_code:
938
- return "์†Œ์Šค์ฝ”๋“œ๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."
939
-
940
- prompt = f"""
941
- ๋‹ค์Œ ์ŠคํŽ˜์ด์Šค์˜ ์†Œ์Šค์ฝ”๋“œ๋ฅผ ๋ถ„์„ํ•ด์ฃผ์„ธ์š”:
942
-
943
- ```
944
- {source_code[:4000]}
945
- ```
946
-
947
- ๋‹ค์Œ ํ•ญ๋ชฉ์„ ๊ฐ๊ฐ ํ•œ ์ค„๋กœ ์š”์•ฝํ•ด์ฃผ์„ธ์š”:
948
- 1. ๊ฐœ์š”:
949
- 2. ์š”์•ฝ:
950
- 3. ํŠน์ง• ๋ฐ ์žฅ์ :
951
- 4. ์‚ฌ์šฉ ๋Œ€์ƒ:
952
- 5. ์‚ฌ์šฉ ๋ฐฉ๋ฒ•:
953
- 6. ์œ ์‚ฌ ์„œ๋น„์Šค์™€์˜ ์ฐจ๋ณ„์ :
954
- """
955
-
956
- messages = [
957
- {"role": "system", "content": "์†Œ์Šค์ฝ”๋“œ ๋ถ„์„ ์ „๋ฌธ๊ฐ€์ž…๋‹ˆ๋‹ค."},
958
- {"role": "user", "content": prompt}
959
- ]
960
-
961
- response = hf_client.chat_completion(
962
- messages,
963
- max_tokens=3900,
964
- temperature=0.3
965
- )
966
-
967
- return response.choices[0].message.content
968
-
969
- except Exception as e:
970
- return f"๋ถ„์„ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}"
971
-
972
-
973
-
974
- def create_editable_space_analysis(progress=gr.Progress()) -> List[str]:
975
- """24๊ฐœ ์ŠคํŽ˜์ด์Šค ๋ถ„์„ ํ…์ŠคํŠธ ์ƒ์„ฑ"""
976
- try:
977
- progress(0, desc="์ŠคํŽ˜์ด์Šค ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๋Š” ์ค‘...")
978
-
979
- url = "https://huggingface.co/api/spaces"
980
- response = requests.get(url, params={'full': 'true', 'limit': 24})
981
- response.raise_for_status()
982
- spaces = response.json()[:24]
983
-
984
- analysis_texts = []
985
-
986
- for idx, space in enumerate(spaces):
987
- progress((idx + 1) / 24, desc=f"๋ถ„์„ ์ค‘... {idx+1}/24")
988
- try:
989
- source = get_space_source(space['id'])
990
- source_code = source["app.py"] or source["index.html"]
991
-
992
- # ํ”„๋กœ์ ํŠธ๋ช…๋งŒ ์ถ”์ถœ
993
- project_name = space['id'].split('/')[-1]
994
-
995
- prompt = f"""
996
- ๋‹ค์Œ HuggingFace ์ŠคํŽ˜์ด์Šค๋ฅผ ๋ถ„์„ํ•˜์—ฌ ๋‰ด์Šค ๋ฆฌํฌํŠธ ํ˜•์‹์œผ๋กœ ์„ค๋ช…ํ•ด์ฃผ์„ธ์š”:
997
- ์‹œ์ž‘์€ ๋ฐ˜๋“œ์‹œ "์˜ค๋Š˜์˜ ์ธ๊ธฐ์ˆœ์œ„ {idx + 1}์œ„์ธ {project_name}์ž…๋‹ˆ๋‹ค."๋กœ ์‹œ์ž‘ํ•˜๊ณ ,
998
- ์ด์–ด์„œ ์ฃผ์š” ๊ธฐ๋Šฅ, ํŠน์ง•, ํ™œ์šฉ๋ฐฉ์•ˆ์„ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์„ค๋ช…ํ•ด์ฃผ์„ธ์š”.
999
-
1000
- ์†Œ์Šค์ฝ”๋“œ:
1001
- ```
1002
- {source_code[:1500]}
1003
- ```
1004
- """
1005
-
1006
- messages = [
1007
- {"role": "system", "content": "AI ๊ธฐ์ˆ  ์ „๋ฌธ ๋‰ด์Šค ๋ฆฌํฌํ„ฐ์ž…๋‹ˆ๋‹ค."},
1008
- {"role": "user", "content": prompt}
1009
- ]
1010
-
1011
- response = hf_client.chat_completion(
1012
- messages,
1013
- max_tokens=2000,
1014
- temperature=0.7
1015
- )
1016
-
1017
- analysis_texts.append(response.choices[0].message.content.strip())
1018
-
1019
- except Exception as e:
1020
- analysis_texts.append(f"์˜ค๋Š˜์˜ ์ธ๊ธฐ์ˆœ์œ„ {idx + 1}์œ„์ธ {project_name}์ž…๋‹ˆ๋‹ค.")
1021
-
1022
- return analysis_texts
1023
-
1024
- except Exception as e:
1025
- return [f"์ˆœ์œ„ {i+1}์œ„ ๋ถ„์„์„ ์ค€๋น„์ค‘์ž…๋‹ˆ๋‹ค." for i in range(24)]
1026
-
1027
- def generate_video(texts: List[str], progress=gr.Progress()) -> str:
1028
- """์˜์ƒ ์ƒ์„ฑ"""
1029
- try:
1030
- from PIL import Image, ImageDraw
1031
-
1032
- # ์ž„์‹œ ๋””๋ ‰ํ† ๋ฆฌ ์ƒ์„ฑ
1033
- temp_dir = tempfile.mkdtemp()
1034
- output_path = os.path.join(temp_dir, "output_video.mp4")
1035
- clips = []
1036
-
1037
- try:
1038
- # ์ธํŠธ๋กœ ์ƒ์„ฑ
1039
- intro_text = texts[0]
1040
- intro_img = Image.new('RGB', (800, 600), (0, 0, 0))
1041
- draw = ImageDraw.Draw(intro_img)
1042
-
1043
- # ์ธํŠธ๋กœ ํ…์ŠคํŠธ ๊ทธ๋ฆฌ๊ธฐ
1044
- margin = 40
1045
- y_position = margin
1046
- for line in intro_text.split('\n'):
1047
- draw.text((margin, y_position), line, fill=(255, 255, 255))
1048
- y_position += 40
1049
-
1050
- # ์ธํŠธ๋กœ ํด๋ฆฝ ์ƒ์„ฑ
1051
- intro_clip = ImageClip(np.array(intro_img))
1052
- intro_audio = gTTS(text=intro_text, lang='ko', slow=False)
1053
- intro_audio_path = os.path.join(temp_dir, "intro.mp3")
1054
- intro_audio.save(intro_audio_path)
1055
- intro_audio_clip = AudioFileClip(intro_audio_path)
1056
- intro_clip = intro_clip.set_duration(intro_audio_clip.duration)
1057
- intro_clip = intro_clip.set_audio(intro_audio_clip)
1058
- clips.append(intro_clip)
1059
-
1060
- # ๊ฐ ์ŠคํŽ˜์ด์Šค๋ณ„ ํด๋ฆฝ ์ƒ์„ฑ
1061
- for idx, text in enumerate(texts[1:], 1):
1062
- progress((idx / len(texts)), desc=f"์˜์ƒ ์ƒ์„ฑ ์ค‘... {idx}/{len(texts)}")
1063
-
1064
- # ์ŠคํŽ˜์ด์Šค URL ๊ตฌ์„ฑ
1065
- space_id = text.split("์ธ๊ธฐ์ˆœ์œ„")[1].split("์œ„์ธ")[0].strip()
1066
- space_url = f"https://huggingface.co/spaces/{space_id}"
1067
-
1068
- # ์Šคํฌ๋ฆฐ์ƒท ์บก์ฒ˜ (5์ดˆ ๋Œ€๊ธฐ)
1069
- time.sleep(5) # ์ถฉ๋ถ„ํ•œ ๋กœ๋”ฉ ์‹œ๊ฐ„ ๋ณด์žฅ
1070
- screenshot_base64 = get_cached_screenshot(space_url)
1071
-
1072
- if screenshot_base64:
1073
- # ์Šคํฌ๋ฆฐ์ƒท ์ด๋ฏธ์ง€ ์ƒ์„ฑ
1074
- screenshot_data = base64.b64decode(screenshot_base64)
1075
- screenshot = Image.open(BytesIO(screenshot_data))
1076
- screenshot = screenshot.resize((800, 600), Image.Resampling.LANCZOS)
1077
- else:
1078
- # ์Šคํฌ๋ฆฐ์ƒท ์‹คํŒจ ์‹œ ๊ฒ€์€ ๋ฐฐ๊ฒฝ
1079
- screenshot = Image.new('RGB', (800, 600), (0, 0, 0))
1080
-
1081
- # ํ…์ŠคํŠธ ์˜ค๋ฒ„๋ ˆ์ด
1082
- draw = ImageDraw.Draw(screenshot)
1083
- text_lines = text.split('. ')
1084
- y_position = 500 # ํ™”๋ฉด ํ•˜๋‹จ์— ํ…์ŠคํŠธ ๋ฐฐ์น˜
1085
- for line in text_lines:
1086
- if not line.endswith('.'):
1087
- line += '.' # ๋ฌธ์žฅ ๋์— ๋งˆ์นจํ‘œ ์ถ”๊ฐ€
1088
- draw.text((20, y_position), line, fill=(255, 255, 255))
1089
- y_position += 30
1090
-
1091
- # ์Œ์„ฑ ์ƒ์„ฑ
1092
- tts = gTTS(text=text, lang='ko', slow=False)
1093
- audio_path = os.path.join(temp_dir, f"audio_{idx}.mp3")
1094
- tts.save(audio_path)
1095
- audio_clip = AudioFileClip(audio_path)
1096
-
1097
- # ์ด๋ฏธ์ง€ ํด๋ฆฝ ์ƒ์„ฑ ๋ฐ ์˜ค๋””์˜ค ๊ฒฐํ•ฉ
1098
- image_clip = ImageClip(np.array(screenshot))
1099
- image_clip = image_clip.set_duration(audio_clip.duration)
1100
- video_clip = image_clip.set_audio(audio_clip)
1101
- clips.append(video_clip)
1102
-
1103
- # ๋ชจ๋“  ํด๋ฆฝ ์—ฐ๊ฒฐ
1104
- final_clip = concatenate_videoclips(clips)
1105
-
1106
- # MP4๋กœ ์ €์žฅ
1107
- final_clip.write_videofile(
1108
- output_path,
1109
- fps=24,
1110
- codec='libx264',
1111
- audio_codec='aac',
1112
- threads=4
1113
- )
1114
-
1115
- return output_path
1116
-
1117
- finally:
1118
- # ํด๋ฆฝ ์ •๋ฆฌ
1119
- for clip in clips:
1120
- try:
1121
- clip.close()
1122
- except:
1123
- pass
1124
-
1125
- except Exception as e:
1126
- print(f"Error generating video: {e}")
1127
- traceback.print_exc()
1128
- return ""
1129
-
1130
- finally:
1131
- # ์ž„์‹œ ํŒŒ์ผ ์ •๋ฆฌ
1132
- try:
1133
- shutil.rmtree(temp_dir)
1134
- except Exception as e:
1135
- print(f"Error cleaning up temp files: {e}")
1136
-
1137
- def create_interface():
1138
- # ๋ถ„์„์šฉ ์ปดํฌ๋„ŒํŠธ ๋ฆฌ์ŠคํŠธ ์ดˆ๊ธฐํ™”
1139
- analysis_boxes = []
1140
- space_images = []
1141
-
1142
- with gr.Blocks(title="HuggingFace Trending Board", css="""
1143
- .search-sort-container {
1144
- background: linear-gradient(135deg, rgba(255,255,255,0.95), rgba(240,240,255,0.95));
1145
- border-radius: 15px;
1146
- padding: 20px;
1147
- margin: 10px 0;
1148
- box-shadow: 0 4px 6px rgba(0,0,0,0.1);
1149
- overflow: visible;
1150
- }
1151
- .search-box {
1152
- border: 2px solid #e1e1e1;
1153
- border-radius: 10px;
1154
- padding: 12px;
1155
- transition: all 0.3s ease;
1156
- background: linear-gradient(135deg, #ffffff, #f8f9ff);
1157
- width: 100%;
1158
- }
1159
- .search-box:focus {
1160
- border-color: #7b61ff;
1161
- box-shadow: 0 0 0 2px rgba(123,97,255,0.2);
1162
- background: linear-gradient(135deg, #ffffff, #f0f3ff);
1163
- }
1164
- .refresh-btn {
1165
- background: linear-gradient(135deg, #7b61ff, #6366f1);
1166
- color: white;
1167
- border: none;
1168
- padding: 10px 20px;
1169
- border-radius: 10px;
1170
- cursor: pointer;
1171
- transition: all 0.3s ease;
1172
- width: 120px;
1173
- height: 80px !important;
1174
- display: flex;
1175
- align-items: center;
1176
- justify-content: center;
1177
- margin-left: auto;
1178
- font-size: 1.2em !important;
1179
- box-shadow: 0 4px 6px rgba(0,0,0,0.1);
1180
- }
1181
- .refresh-btn:hover {
1182
- transform: translateY(-2px);
1183
- box-shadow: 0 6px 12px rgba(0,0,0,0.2);
1184
- background: linear-gradient(135deg, #8b71ff, #7376f1);
1185
- }
1186
- """) as interface:
1187
-
1188
- gr.Markdown("""
1189
- # ๐Ÿค— HuggingFace Trending Board
1190
- <div style='margin-bottom: 20px; padding: 10px; background: linear-gradient(135deg, rgba(123,97,255,0.1), rgba(99,102,241,0.1)); border-radius: 10px;'>
1191
- Explore, search, and sort through the Shows Top 300 Trending spaces with AI Ratings
1192
- </div>
1193
- """)
1194
-
1195
- with gr.Tabs() as tabs:
1196
- # Spaces ํƒญ
1197
- with gr.Tab("๐ŸŽฏ Trending Spaces"):
1198
- with gr.Row(elem_classes="search-sort-container"):
1199
- with gr.Column(scale=2):
1200
- spaces_search = gr.Textbox(
1201
- label="๐Ÿ” Search Spaces",
1202
- placeholder="Enter keywords to search...",
1203
- elem_classes="search-box"
1204
- )
1205
- with gr.Column(scale=2):
1206
- spaces_sort = gr.Radio(
1207
- choices=["rank", "rising_rate", "popularity"],
1208
- value="rank",
1209
- label="Sort by",
1210
- interactive=True
1211
- )
1212
- with gr.Column(scale=1):
1213
- spaces_refresh_btn = gr.Button(
1214
- "๐Ÿ”„ Refresh",
1215
- variant="primary",
1216
- elem_classes="refresh-btn"
1217
- )
1218
- spaces_gallery = gr.HTML()
1219
- spaces_status = gr.Markdown("Loading...")
1220
-
1221
- # Models ํƒญ
1222
- with gr.Tab("๐Ÿค– Trending Models"):
1223
- with gr.Row(elem_classes="search-sort-container"):
1224
- with gr.Column(scale=2):
1225
- models_search = gr.Textbox(
1226
- label="๐Ÿ” Search Models",
1227
- placeholder="Enter keywords to search...",
1228
- elem_classes="search-box"
1229
- )
1230
- with gr.Column(scale=2):
1231
- models_sort = gr.Radio(
1232
- choices=["rank", "rising_rate", "popularity"],
1233
- value="rank",
1234
- label="Sort by",
1235
- interactive=True
1236
- )
1237
- with gr.Column(scale=1):
1238
- models_refresh_btn = gr.Button(
1239
- "๐Ÿ”„ Refresh",
1240
- variant="primary",
1241
- elem_classes="refresh-btn"
1242
- )
1243
- models_gallery = gr.HTML()
1244
- models_status = gr.Markdown("Loading...")
1245
-
1246
- # Datasets ํƒญ
1247
- with gr.Tab("๐Ÿ“Š Trending Datasets"):
1248
- with gr.Row(elem_classes="search-sort-container"):
1249
- with gr.Column(scale=2):
1250
- datasets_search = gr.Textbox(
1251
- label="๐Ÿ” Search Datasets",
1252
- placeholder="Enter keywords to search...",
1253
- elem_classes="search-box"
1254
- )
1255
- with gr.Column(scale=2):
1256
- datasets_sort = gr.Radio(
1257
- choices=["rank", "rising_rate", "popularity"],
1258
- value="rank",
1259
- label="Sort by",
1260
- interactive=True
1261
- )
1262
- with gr.Column(scale=1):
1263
- datasets_refresh_btn = gr.Button(
1264
- "๐Ÿ”„ Refresh",
1265
- variant="primary",
1266
- elem_classes="refresh-btn"
1267
- )
1268
- datasets_gallery = gr.HTML()
1269
- datasets_status = gr.Markdown("Loading...")
1270
-
1271
- # ๋ถ„์„ ํƒญ
1272
- with gr.Tab("๐Ÿ” Top 24 Spaces Analysis"):
1273
- with gr.Row(elem_classes="search-sort-container"):
1274
- analysis_refresh_btn = gr.Button(
1275
- "๐Ÿ”„ Analyze All 24 Spaces",
1276
- variant="primary",
1277
- elem_classes="refresh-btn"
1278
- )
1279
-
1280
- # ์ธํŠธ๋กœ ์„น์…˜
1281
- with gr.Row():
1282
- with gr.Column(scale=3):
1283
- intro_text = gr.Textbox(
1284
- value="์•ˆ๋…•ํ•˜์„ธ์š”. ๋งค์ผ ๊ธ€๋กœ๋ฒŒ ์ตœ์‹  AI ์ธ๊ธฐ ํŠธ๋ Œ๋“œ ์„œ๋น„์Šค๋ฅผ ์•Œ์•„๋ณด๋Š” '๋ฐ์ผ๋ฆฌ AI ํŠธ๋ Œ๋”ฉ' ๋‰ด์Šค์ž…๋‹ˆ๋‹ค. ์˜ค๋Š˜์˜ ํ—ˆ๊น…ํŽ˜์ด์Šค ์ธ๊ธฐ ์ˆœ์œ„ 1์œ„๋ถ€ํ„ฐ 24์œ„๊นŒ์ง€, ๋ถ„์„๊ณผ ํ•ต์‹ฌ ๋‚ด์šฉ์„ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.",
1285
- label="์ธํŠธ๋กœ ํ…์ŠคํŠธ",
1286
- lines=4
1287
- )
1288
- with gr.Column(scale=1):
1289
- # ์ธํŠธ๋กœ ์ด๋ฏธ์ง€ ๋ถ€๋ถ„ ์ˆ˜์ •
1290
- intro_image = gr.Image(
1291
- label="์ธํŠธ๋กœ ์ด๋ฏธ์ง€",
1292
- type="filepath",
1293
- height=150,
1294
- elem_classes="image-upload-box"
1295
- )
1296
-
1297
-
1298
- # 24๊ฐœ์˜ ์ŠคํŽ˜์ด์Šค ๋ถ„์„ ์„น์…˜
1299
- for i in range(24):
1300
- with gr.Row():
1301
- with gr.Column(scale=3):
1302
- text_box = gr.Textbox(
1303
- label=f"Space #{i+1}",
1304
- lines=3
1305
- )
1306
- analysis_boxes.append(text_box)
1307
- with gr.Column(scale=1):
1308
-
1309
- img = gr.Image(
1310
- label=f"Space #{i+1} Screenshot",
1311
- type="filepath",
1312
- height=150,
1313
- elem_classes="image-upload-box",
1314
- interactive=True
1315
- )
1316
- space_images.append(img)
1317
-
1318
- # ๋น„๋””์˜ค ์ƒ์„ฑ ์„น์…˜
1319
- with gr.Row():
1320
- with gr.Column():
1321
- generate_btn = gr.Button(
1322
- "๐ŸŽฌ ์˜์ƒ ์ƒ์„ฑ",
1323
- variant="primary",
1324
- size="lg"
1325
- )
1326
- video_output = gr.Video(label="์ƒ์„ฑ๋œ ์˜์ƒ")
1327
-
1328
- # ์ด๋ฏธ์ง€ ์ผ๊ด„ ์—…๋ฐ์ดํŠธ ๋ฒ„ํŠผ ์ถ”๊ฐ€
1329
- with gr.Row():
1330
- update_images_btn = gr.Button(
1331
- "๐Ÿ”„ Update Screenshots",
1332
- variant="secondary"
1333
- )
1334
-
1335
- def update_screenshots():
1336
- """์Šคํฌ๋ฆฐ์ƒท ์ผ๊ด„ ์—…๋ฐ์ดํŠธ"""
1337
- try:
1338
- url = "https://huggingface.co/api/spaces"
1339
- response = requests.get(url, params={'full': 'true', 'limit': 24})
1340
- spaces = response.json()[:24]
1341
-
1342
- image_paths = []
1343
- temp_dir = Path("temp_screenshots")
1344
- temp_dir.mkdir(exist_ok=True)
1345
-
1346
- for idx, space in enumerate(spaces):
1347
- space_url = f"https://huggingface.co/spaces/{space['id']}"
1348
- time.sleep(5) # ์ถฉ๋ถ„ํ•œ ๋กœ๋”ฉ ์‹œ๊ฐ„
1349
- screenshot_base64 = get_cached_screenshot(space_url)
1350
-
1351
- if screenshot_base64:
1352
- try:
1353
- img_data = base64.b64decode(screenshot_base64)
1354
- screenshot_path = temp_dir / f"screenshot_{idx+1}.jpg"
1355
- with open(screenshot_path, 'wb') as f:
1356
- f.write(img_data)
1357
- image_paths.append(str(screenshot_path))
1358
- except Exception as e:
1359
- print(f"Screenshot save error: {e}")
1360
- image_paths.append(None)
1361
- else:
1362
- image_paths.append(None)
1363
-
1364
- return image_paths
1365
- except Exception as e:
1366
- print(f"Update screenshots error: {e}")
1367
- return [None] * 24
1368
-
1369
- # ์ด๋ฏธ์ง€ ์—…๋ฐ์ดํŠธ ๋ฒ„ํŠผ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ
1370
- update_images_btn.click(
1371
- fn=update_screenshots,
1372
- outputs=space_images
1373
- )
1374
-
1375
-
1376
-
1377
- # Event handlers
1378
- spaces_refresh_btn.click(
1379
- fn=get_trending_spaces,
1380
- inputs=[spaces_search, spaces_sort],
1381
- outputs=[spaces_gallery, spaces_status]
1382
- )
1383
-
1384
- models_refresh_btn.click(
1385
- fn=get_models,
1386
- inputs=[models_search, models_sort],
1387
- outputs=[models_gallery, models_status]
1388
- )
1389
-
1390
- datasets_refresh_btn.click(
1391
- fn=get_datasets,
1392
- inputs=[datasets_search, datasets_sort],
1393
- outputs=[datasets_gallery, datasets_status]
1394
- )
1395
-
1396
- # ๊ฒ€์ƒ‰์–ด ๋ณ€๊ฒฝ ์‹œ ์ž๋™ ์ƒˆ๋กœ๊ณ ์นจ
1397
- spaces_search.change(
1398
- fn=get_trending_spaces,
1399
- inputs=[spaces_search, spaces_sort],
1400
- outputs=[spaces_gallery, spaces_status]
1401
- )
1402
-
1403
- models_search.change(
1404
- fn=get_models,
1405
- inputs=[models_search, models_sort],
1406
- outputs=[models_gallery, models_status]
1407
- )
1408
-
1409
- datasets_search.change(
1410
- fn=get_datasets,
1411
- inputs=[datasets_search, datasets_sort],
1412
- outputs=[datasets_gallery, datasets_status]
1413
- )
1414
-
1415
- # ์ •๋ ฌ ๋ฐฉ์‹ ๋ณ€๊ฒฝ ์‹œ ์ž๋™ ์ƒˆ๋กœ๊ณ ์นจ
1416
- spaces_sort.change(
1417
- fn=get_trending_spaces,
1418
- inputs=[spaces_search, spaces_sort],
1419
- outputs=[spaces_gallery, spaces_status]
1420
- )
1421
-
1422
- models_sort.change(
1423
- fn=get_models,
1424
- inputs=[models_search, models_sort],
1425
- outputs=[models_gallery, models_status]
1426
- )
1427
-
1428
- datasets_sort.change(
1429
- fn=get_datasets,
1430
- inputs=[datasets_search, datasets_sort],
1431
- outputs=[datasets_gallery, datasets_status]
1432
- )
1433
-
1434
- # ๋ถ„์„ ํƒญ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ
1435
- analysis_refresh_btn.click(
1436
- fn=on_analyze,
1437
- outputs=analysis_boxes + space_images
1438
- )
1439
-
1440
- generate_btn.click(
1441
- fn=on_generate_video,
1442
- inputs=[intro_text] + analysis_boxes, # ํ…์ŠคํŠธ ์ž…๋ ฅ๋งŒ ์‚ฌ์šฉ
1443
- outputs=video_output
1444
- )
1445
-
1446
-
1447
- # ์ดˆ๊ธฐ ๋ฐ์ดํ„ฐ ๋กœ๋“œ
1448
- interface.load(
1449
- fn=get_trending_spaces,
1450
- inputs=[spaces_search, spaces_sort],
1451
- outputs=[spaces_gallery, spaces_status]
1452
- )
1453
- interface.load(
1454
- fn=get_models,
1455
- inputs=[models_search, models_sort],
1456
- outputs=[models_gallery, models_status]
1457
- )
1458
- interface.load(
1459
- fn=get_datasets,
1460
- inputs=[datasets_search, datasets_sort],
1461
- outputs=[datasets_gallery, datasets_status]
1462
- )
1463
- interface.load(
1464
- fn=on_analyze,
1465
- outputs=analysis_boxes + space_images
1466
- )
1467
-
1468
- return interface
1469
-
1470
-
1471
- def on_analyze(progress=gr.Progress()):
1472
- """๋ถ„์„ ์‹คํ–‰ ๋ฐ ํ…์ŠคํŠธ๋ฐ•์Šค/์ด๋ฏธ์ง€ ์—…๋ฐ์ดํŠธ"""
1473
- try:
1474
- url = "https://huggingface.co/api/spaces"
1475
- response = requests.get(url, params={'full': 'true', 'limit': 24})
1476
- response.raise_for_status()
1477
- spaces = response.json()[:24]
1478
-
1479
- # ๊ฒฐ๊ณผ๋ฅผ ์ €์žฅํ•  ๋ฆฌ์ŠคํŠธ
1480
- text_results = []
1481
- image_results = []
1482
-
1483
- # ์ž„์‹œ ๋””๋ ‰ํ† ๋ฆฌ ์ƒ์„ฑ
1484
- temp_dir = Path("temp_screenshots")
1485
- temp_dir.mkdir(exist_ok=True)
1486
-
1487
- for idx, space in enumerate(spaces):
1488
- progress((idx + 1) / 24, desc=f"๋ถ„์„ ์ค‘... {idx+1}/24")
1489
- try:
1490
- # ์Šคํฌ๋ฆฐ์ƒท ์บก์ฒ˜
1491
- space_url = f"https://huggingface.co/spaces/{space['id']}"
1492
- project_name = space['id'].split('/')[-1]
1493
-
1494
- # ํ…์ŠคํŠธ ๋ถ„์„
1495
- source = get_space_source(space['id'])
1496
- source_code = source["app.py"] or source["index.html"]
1497
-
1498
- prompt = f"""
1499
- ๋‹ค์Œ HuggingFace ์ŠคํŽ˜์ด์Šค๋ฅผ ๋‰ด์Šค ๋ฆฌํฌํŠธ ํ˜•์‹์œผ๋กœ ์„ค๋ช…ํ•ด์ฃผ์„ธ์š”:
1500
- ์‹œ์ž‘์€ ๋ฐ˜๋“œ์‹œ "์˜ค๋Š˜์˜ ์ธ๊ธฐ์ˆœ์œ„ {idx + 1}์œ„์ธ {project_name}์ž…๋‹ˆ๋‹ค."๋กœ ์‹œ์ž‘ํ•˜๊ณ ,
1501
- ์ด์–ด์„œ ์ฃผ์š” ๊ธฐ๋Šฅ๊ณผ ํŠน์ง•์„ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์„ค๋ช…ํ•ด์ฃผ์„ธ์š”.
1502
- """
1503
-
1504
- messages = [
1505
- {"role": "system", "content": "AI ๊ธฐ์ˆ  ์ „๋ฌธ ๋‰ด์Šค ๋ฆฌํฌํ„ฐ์ž…๋‹ˆ๋‹ค."},
1506
- {"role": "user", "content": prompt}
1507
- ]
1508
-
1509
- response = hf_client.chat_completion(
1510
- messages,
1511
- max_tokens=500,
1512
- temperature=0.7
1513
- )
1514
-
1515
- analysis = response.choices[0].message.content.strip()
1516
- text_results.append(analysis)
1517
-
1518
- # ์Šคํฌ๋ฆฐ์ƒท ์ฒ˜๋ฆฌ
1519
- try:
1520
- screenshot_path = temp_dir / f"space_{idx+1}.jpg"
1521
- screenshot_base64 = get_cached_screenshot(space_url)
1522
-
1523
- if screenshot_base64:
1524
- img_data = base64.b64decode(screenshot_base64)
1525
- with open(screenshot_path, 'wb') as f:
1526
- f.write(img_data)
1527
- image_results.append(str(screenshot_path))
1528
- else:
1529
- image_results.append(None)
1530
- except Exception as e:
1531
- print(f"Screenshot error for {space['id']}: {e}")
1532
- image_results.append(None)
1533
-
1534
- except Exception as e:
1535
- print(f"Error processing space {space['id']}: {e}")
1536
- text_results.append(f"์ˆœ์œ„ {idx + 1}์œ„ ๋ถ„์„์„ ์ค€๋น„์ค‘์ž…๋‹ˆ๋‹ค.")
1537
- image_results.append(None)
1538
-
1539
- # ๋นˆ ๊ฒฐ๊ณผ ์ฑ„์šฐ๊ธฐ
1540
- while len(text_results) < 24:
1541
- text_results.append("๋ถ„์„์„ ์ค€๋น„์ค‘์ž…๋‹ˆ๋‹ค.")
1542
- while len(image_results) < 24:
1543
- image_results.append(None)
1544
-
1545
- # ๋ชจ๋“  ๊ฒฐ๊ณผ ํ•ฉ์น˜๊ธฐ
1546
- all_results = []
1547
- for text, image in zip(text_results, image_results):
1548
- all_results.extend([text, image])
1549
-
1550
- return all_results
1551
-
1552
- except Exception as e:
1553
- print(f"Analysis error: {e}")
1554
- return ["๋ถ„์„์„ ์ค€๋น„์ค‘์ž…๋‹ˆ๋‹ค." for _ in range(48)]
1555
-
1556
- # ์ธํ„ฐํŽ˜์ด์Šค์—์„œ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ์ˆ˜์ •
1557
- analysis_refresh_btn.click(
1558
- fn=on_analyze,
1559
- outputs=analysis_boxes + space_images
1560
- )
1561
-
1562
- # ์˜์ƒ ์ƒ์„ฑ ํ•จ์ˆ˜ ์ˆ˜์ •
1563
- def on_generate_video(intro_text, *texts):
1564
- """์˜์ƒ ์ƒ์„ฑ"""
1565
- try:
1566
- temp_dir = tempfile.mkdtemp()
1567
- clips = []
1568
-
1569
- # ์ธํŠธ๋กœ ์ฒ˜๋ฆฌ
1570
- intro_img = Image.new('RGB', (800, 600), (0, 0, 0))
1571
- draw = ImageDraw.Draw(intro_img)
1572
- draw.text((40, 40), intro_text, fill=(255, 255, 255))
1573
-
1574
- # ์ธํŠธ๋กœ ํด๋ฆฝ ์ƒ์„ฑ
1575
- intro_audio = gTTS(text=intro_text, lang='ko', slow=False)
1576
- intro_audio_path = os.path.join(temp_dir, "intro.mp3")
1577
- intro_audio.save(intro_audio_path)
1578
- intro_audio_clip = AudioFileClip(intro_audio_path)
1579
-
1580
- intro_clip = ImageClip(np.array(intro_img))
1581
- intro_clip = intro_clip.set_duration(intro_audio_clip.duration)
1582
- intro_clip = intro_clip.set_audio(intro_audio_clip)
1583
- clips.append(intro_clip)
1584
-
1585
- # ๊ฐ ์ŠคํŽ˜์ด์Šค ์ฒ˜๋ฆฌ
1586
- for idx, text in enumerate(texts):
1587
- if not text:
1588
- continue
1589
-
1590
- # ์ด๋ฏธ์ง€ ์ƒ์„ฑ
1591
- img = Image.new('RGB', (800, 600), (0, 0, 0))
1592
- draw = ImageDraw.Draw(img)
1593
- draw.text((40, 40), text, fill=(255, 255, 255))
1594
-
1595
- # ์Œ์„ฑ ์ƒ์„ฑ
1596
- tts = gTTS(text=text, lang='ko', slow=False)
1597
- audio_path = os.path.join(temp_dir, f"audio_{idx}.mp3")
1598
- tts.save(audio_path)
1599
- audio_clip = AudioFileClip(audio_path)
1600
-
1601
- # ํด๋ฆฝ ์ƒ์„ฑ
1602
- video_clip = ImageClip(np.array(img))
1603
- video_clip = video_clip.set_duration(audio_clip.duration)
1604
- video_clip = video_clip.set_audio(audio_clip)
1605
- clips.append(video_clip)
1606
-
1607
- # ์ตœ์ข… ์˜์ƒ ์ƒ์„ฑ
1608
- final_clip = concatenate_videoclips(clips)
1609
- output_path = "output_video.mp4"
1610
- final_clip.write_videofile(
1611
- output_path,
1612
- fps=24,
1613
- codec='libx264',
1614
- audio_codec='aac'
1615
- )
1616
-
1617
- return output_path
1618
-
1619
- except Exception as e:
1620
- print(f"Video generation error: {e}")
1621
- return None
1622
-
1623
- finally:
1624
- if 'temp_dir' in locals():
1625
- shutil.rmtree(temp_dir)
1626
-
1627
- if __name__ == "__main__":
1628
- try:
1629
- CACHE_DIR.mkdir(exist_ok=True)
1630
- cleanup_cache()
1631
- demo = create_interface()
1632
- demo.launch(
1633
- share=True,
1634
- inbrowser=True,
1635
- show_api=False,
1636
- max_threads=4
1637
- )
1638
- except Exception as e:
1639
- print(f"Application error: {e}")