Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -12,7 +12,7 @@ print(f"API 토큰: {TOKEN[:4]}... (마스킹됨)")
|
|
12 |
print(f"팀 ID: {TEAM if TEAM else '없음'}")
|
13 |
|
14 |
# ───────────────────── 2. BEST 데이터 ────────────────────
|
15 |
-
BEST_FILE, PER_PAGE = "best_games.json",
|
16 |
def _init_best():
|
17 |
if not os.path.exists(BEST_FILE):
|
18 |
json.dump([], open(BEST_FILE, "w"))
|
@@ -196,9 +196,10 @@ def html(cards, pg, total):
|
|
196 |
css=r"""
|
197 |
<style>
|
198 |
body{margin:0;font-family:Poppins,sans-serif;background:linear-gradient(135deg,#C5E8FF 0%,#FFD6E0 100%);}
|
199 |
-
.grid{display:grid;grid-template-columns:repeat(3,1fr);gap:28px 24px;margin:0 20px
|
200 |
-
@media(max-width:1024px){.grid{grid-template-columns:repeat(
|
201 |
-
@media(max-width:
|
|
|
202 |
.card{background:#fff;border-radius:18px;overflow:hidden;box-shadow:0 10px 25px rgba(0,0,0,.08);transition:.3s}
|
203 |
.card:hover{transform:translateY(-6px);box-shadow:0 16px 40px rgba(0,0,0,.12)}
|
204 |
.hdr{padding:20px 24px;background:rgba(255,255,255,.75);backdrop-filter:blur(8px);border-bottom:1px solid #eee;}
|
@@ -209,7 +210,7 @@ def html(cards, pg, total):
|
|
209 |
transform:scale(.7);transform-origin:top left;border:0;}
|
210 |
.foot{padding:14px 24px;background:rgba(255,255,255,.85);backdrop-filter:blur(8px);text-align:right;}
|
211 |
.link{font-size:.85rem;font-weight:600;color:#4a6dd8;text-decoration:none;}
|
212 |
-
.cnt{text-align:center;font-size:.85rem;color:#555;margin:10px 0
|
213 |
</style>"""
|
214 |
|
215 |
h=css+"<div class='grid'>"
|
@@ -228,7 +229,7 @@ def html(cards, pg, total):
|
|
228 |
# ───────────────────── 7. Gradio Blocks UI ───────────────
|
229 |
def build():
|
230 |
_init_best()
|
231 |
-
with gr.Blocks(title="Vibe Game Craft", css="body{overflow-x:hidden;}") as demo:
|
232 |
gr.Markdown("<h1 style='text-align:center;padding:32px 0 0;color:#333;'>🎮 Vibe Game Craft</h1>")
|
233 |
|
234 |
# URL 추가 인터페이스
|
@@ -254,27 +255,6 @@ def build():
|
|
254 |
d, t = page(_load_best(), p)
|
255 |
return html(d, p, t), p
|
256 |
|
257 |
-
def add_url():
|
258 |
-
title = title_input.value
|
259 |
-
url = url_input.value
|
260 |
-
|
261 |
-
if not title or not url:
|
262 |
-
return "❌ 제목과 URL을 모두 입력해주세요."
|
263 |
-
|
264 |
-
if not url.startswith("http"):
|
265 |
-
url = "https://" + url
|
266 |
-
|
267 |
-
if add_url_to_best(title, url):
|
268 |
-
return f"✅ URL이 성공적으로 추가되었습니다: {url}"
|
269 |
-
else:
|
270 |
-
return f"❌ URL 추가에 실패했습니다: {url}"
|
271 |
-
|
272 |
-
def run_bypass():
|
273 |
-
if bypass_all_protection():
|
274 |
-
return "✅ 모든 프로젝트의 보호 설정이 비활성화되었습니다."
|
275 |
-
else:
|
276 |
-
return "❌ 일부 또는 모든 프로젝트의 보호 설정 비활성화에 실패했습니다."
|
277 |
-
|
278 |
def prev(b):
|
279 |
b = max(1, b-1)
|
280 |
h, _ = show_best(b)
|
@@ -286,15 +266,27 @@ def build():
|
|
286 |
h, _ = show_best(b)
|
287 |
return h, b
|
288 |
|
|
|
|
|
|
|
|
|
|
|
289 |
# 이벤트 연결
|
290 |
-
add_btn.click(add_url, outputs=[status_msg])
|
291 |
-
b_best.click(show_best, outputs=[out, bp])
|
292 |
b_prev.click(prev, inputs=[bp], outputs=[out, bp])
|
293 |
b_next.click(nxt, inputs=[bp], outputs=[out, bp])
|
294 |
-
b_ref.click(lambda b: show_best(b)[0], inputs=[bp], outputs=[out])
|
295 |
-
b_bypass.click(run_bypass, outputs=[status_msg])
|
296 |
|
297 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
298 |
demo.load(show_best, outputs=[out, bp])
|
299 |
return demo
|
300 |
|
|
|
12 |
print(f"팀 ID: {TEAM if TEAM else '없음'}")
|
13 |
|
14 |
# ───────────────────── 2. BEST 데이터 ────────────────────
|
15 |
+
BEST_FILE, PER_PAGE = "best_games.json", 9
|
16 |
def _init_best():
|
17 |
if not os.path.exists(BEST_FILE):
|
18 |
json.dump([], open(BEST_FILE, "w"))
|
|
|
196 |
css=r"""
|
197 |
<style>
|
198 |
body{margin:0;font-family:Poppins,sans-serif;background:linear-gradient(135deg,#C5E8FF 0%,#FFD6E0 100%);}
|
199 |
+
.grid{display:grid;grid-template-columns:repeat(3,1fr);gap:28px 24px;margin:0 auto;max-width:1200px;padding:20px;}
|
200 |
+
@media(max-width:1024px){.grid{grid-template-columns:repeat(3,1fr);} }
|
201 |
+
@media(max-width:768px){.grid{grid-template-columns:repeat(2,1fr);} }
|
202 |
+
@media(max-width:500px){ .grid{grid-template-columns:1fr;} }
|
203 |
.card{background:#fff;border-radius:18px;overflow:hidden;box-shadow:0 10px 25px rgba(0,0,0,.08);transition:.3s}
|
204 |
.card:hover{transform:translateY(-6px);box-shadow:0 16px 40px rgba(0,0,0,.12)}
|
205 |
.hdr{padding:20px 24px;background:rgba(255,255,255,.75);backdrop-filter:blur(8px);border-bottom:1px solid #eee;}
|
|
|
210 |
transform:scale(.7);transform-origin:top left;border:0;}
|
211 |
.foot{padding:14px 24px;background:rgba(255,255,255,.85);backdrop-filter:blur(8px);text-align:right;}
|
212 |
.link{font-size:.85rem;font-weight:600;color:#4a6dd8;text-decoration:none;}
|
213 |
+
.cnt{text-align:center;font-size:.85rem;color:#555;margin:10px 0 20px;}
|
214 |
</style>"""
|
215 |
|
216 |
h=css+"<div class='grid'>"
|
|
|
229 |
# ───────────────────── 7. Gradio Blocks UI ───────────────
|
230 |
def build():
|
231 |
_init_best()
|
232 |
+
with gr.Blocks(title="Vibe Game Craft", css="body{overflow-x:hidden;} .gradio-container{margin:0 auto; max-width:1280px;}") as demo:
|
233 |
gr.Markdown("<h1 style='text-align:center;padding:32px 0 0;color:#333;'>🎮 Vibe Game Craft</h1>")
|
234 |
|
235 |
# URL 추가 인터페이스
|
|
|
255 |
d, t = page(_load_best(), p)
|
256 |
return html(d, p, t), p
|
257 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
258 |
def prev(b):
|
259 |
b = max(1, b-1)
|
260 |
h, _ = show_best(b)
|
|
|
266 |
h, _ = show_best(b)
|
267 |
return h, b
|
268 |
|
269 |
+
# 페이지 네비게이션 버튼 (하단에 배치)
|
270 |
+
with gr.Row(elem_classes="navigation-buttons"):
|
271 |
+
b_prev = gr.Button("⬅️ 이전 페이지", size="sm")
|
272 |
+
b_next = gr.Button("다음 페이지 ➡️", size="sm")
|
273 |
+
|
274 |
# 이벤트 연결
|
|
|
|
|
275 |
b_prev.click(prev, inputs=[bp], outputs=[out, bp])
|
276 |
b_next.click(nxt, inputs=[bp], outputs=[out, bp])
|
|
|
|
|
277 |
|
278 |
+
# CSS 스타일 추가
|
279 |
+
css = """
|
280 |
+
.navigation-buttons {
|
281 |
+
display: flex;
|
282 |
+
justify-content: center;
|
283 |
+
margin-bottom: 40px;
|
284 |
+
}
|
285 |
+
.navigation-buttons button {
|
286 |
+
margin: 0 10px;
|
287 |
+
min-width: 120px;
|
288 |
+
}
|
289 |
+
"""
|
290 |
demo.load(show_best, outputs=[out, bp])
|
291 |
return demo
|
292 |
|