.ttl { margin: 0; font-size: 0.9rem; font-weight: 600; color: #333; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .date { margin-top: 0; font-size: 0.7rem; color: #777; }import os, re, time, json, datetime, requests, gradio as gr # ───────────────────── 1. 기본 설정 ───────────────────── BEST_FILE, PER_PAGE = "best_games.json", 9 # ───────────────────── 2. BEST 데이터 ──────────────────── def _init_best(): if not os.path.exists(BEST_FILE): json.dump([], open(BEST_FILE, "w")) def _load_best(): try: data = json.load(open(BEST_FILE)) for it in data: if "ts" not in it: it["ts"] = int(it.get("timestamp", time.time())) return data except Exception as e: print(f"BEST 데이터 로드 오류: {e}") return [] def _save_best(data): try: json.dump(data, open(BEST_FILE, "w")) return True except Exception as e: print(f"BEST 데이터 저장 오류: {e}") return False # ───────────────────── 3. URL 추가 기능 ───────────────────── def add_url_to_best(title, url): """사용자가 제공한 URL을 BEST 목록에 추가합니다.""" try: # 현재 BEST 데이터 로드 data = _load_best() # URL이 이미 존재하는지 확인 for item in data: if item.get("url") == url: print(f"URL이 이미 존재합니다: {url}") return False # 새 항목 추가 new_item = { "title": title, "url": url, "ts": int(time.time()), "projectId": "", # 사용자가 직접 추가하므로 projectId 없음 "deploymentId": "" # 사용자가 직접 추가하므로 deploymentId 없음 } data.append(new_item) # 시간순으로 정렬 data = sorted(data, key=lambda x: x["ts"], reverse=True) # 저장 if _save_best(data): print(f"URL이 성공적으로 추가되었습니다: {url}") return True return False except Exception as e: print(f"URL 추가 오류: {str(e)}") return False # ───────────────────── 4. 페이지네이션 ─────────────────── def page(lst, pg): s = (pg-1) * PER_PAGE e = s + PER_PAGE total = (len(lst) + PER_PAGE - 1) // PER_PAGE return lst[s:e], total # ───────────────────── 5. HTML 그리드 ─────────────────── def html(cards, pg, total): if not cards: return "
표시할 배포가 없습니다.
" css = r""" """ h = css + "
" for c in cards: date = datetime.datetime.fromtimestamp(int(c["ts"])).strftime("%Y-%m-%d") h += f"""

{c['title']}

{date}

""" h += "
" return h # ───────────────────── 6. Gradio Blocks UI ─────────────────── def build(): _init_best() with gr.Blocks(title="Vibe Game Craft", css="body{margin:0;padding:0;overflow:hidden;} footer{display:none;} .gradio-container{overflow:hidden;}") as demo: # 상태 및 출력 bp = gr.State(1) out = gr.HTML() # 페이지 네비게이션 with gr.Row(elem_classes="navigation-buttons"): b_prev = gr.Button("◀ 이전", size="lg") b_next = gr.Button("다음 ▶", size="lg") def show_best(p=1): d, t = page(_load_best(), p) return html(d, p, t), p def prev(b): b = max(1, b-1) h, _ = show_best(b) return h, b def nxt(b): maxp = (len(_load_best()) + PER_PAGE - 1) // PER_PAGE b = min(maxp, b+1) h, _ = show_best(b) return h, b # 이벤트 연결 b_prev.click(prev, inputs=[bp], outputs=[out, bp]) b_next.click(nxt, inputs=[bp], outputs=[out, bp]) # 초기 로드 demo.load(show_best, outputs=[out, bp]) # 이벤트 연결 b_prev.click(prev, inputs=[bp], outputs=[out, bp]) b_next.click(nxt, inputs=[bp], outputs=[out, bp]) return demo app = build() if __name__ == "__main__": app.launch()