openfree commited on
Commit
94c297c
·
verified ·
1 Parent(s): 9d8bac2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +30 -191
app.py CHANGED
@@ -1,196 +1,35 @@
1
- import os, json, time, datetime, requests, gradio as gr, re
 
 
 
2
 
3
- # ───────────────────── 1. 기본 설정 ─────────────────────
4
- BEST_FILE, PER_PAGE = "best_games.json", 9 # ❶ 페이지당 9개 유지
5
-
6
- # ───────────────────── 2. BEST 데이터 ────────────────────
7
- def _init_best():
8
- if not os.path.exists(BEST_FILE):
9
- json.dump([], open(BEST_FILE, "w"), ensure_ascii=False)
10
-
11
- def _load_best():
12
  try:
13
- raw = json.load(open(BEST_FILE))
14
- # URL 리스트만 반환
15
- if isinstance(raw, list):
16
- return [u if isinstance(u, str) else u.get("url") for u in raw]
17
- return []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  except Exception as e:
19
- print("BEST 로드 오류:", e)
20
- return []
21
-
22
- def _save_best(lst): # URL 리스트 저장
23
- try:
24
- json.dump(lst, open(BEST_FILE, "w"), ensure_ascii=False, indent=2)
25
- return True
26
- except Exception as e:
27
- print("BEST 저장 오류:", e)
28
- return False
29
-
30
- def add_url_to_best(url: str):
31
- data = _load_best()
32
- if url in data:
33
- return False
34
- data.insert(0, url)
35
- return _save_best(data)
36
-
37
- # ───────────────────── 3. 유틸 ──────────────────────────
38
- def page(lst, pg):
39
- s, e = (pg-1)*PER_PAGE, (pg-1)*PER_PAGE+PER_PAGE
40
- total = (len(lst)+PER_PAGE-1)//PER_PAGE
41
- return lst[s:e], total
42
-
43
- def process_url_for_iframe(url):
44
- """
45
- 반환: (iframe_url, extra_class, alternate_urls)
46
- extra_class : '' | 'huggingface' | 'hfspace'
47
- """
48
- # Hugging Face Spaces embed (Gradio/Streamlit)
49
- if "huggingface.co/spaces" in url:
50
- owner, name = url.rstrip("/").split("/spaces/")[1].split("/")[:2]
51
- return f"https://huggingface.co/spaces/{owner}/{name}/embed", "huggingface", []
52
-
53
- # *.hf.space (정적/static Space 포함)
54
- m = re.match(r"https?://([^/]+)\.hf\.space(/.*)?", url)
55
- if m:
56
- sub, rest = m.groups()
57
- static_url = f"https://{sub}.static.hf.space{rest or ''}"
58
- # alt_urls 에 원본 저장(실패 시 재시도 가능)
59
- return static_url, "hfspace", [url]
60
-
61
- return url, "", []
62
-
63
- # ───────────────────── 6. HTML 그리드 ───────────────────
64
- def html(cards, pg, total):
65
- if not cards:
66
- return "<div style='text-align:center;padding:70px;color:#555;'>표시할 배포가 없습니다.</div>"
67
-
68
- css = r"""
69
- <style>
70
- /* 파스텔 그라디에이션 배경 */
71
- body{
72
- margin:0;padding:0;font-family:Poppins,sans-serif;
73
- background:linear-gradient(135deg,#fdf4ff 0%,#f6fbff 50%,#fffaf4 100%);
74
- background-attachment:fixed;
75
- overflow-x:hidden;overflow-y:auto;
76
- }
77
- .container{width:100%;padding:10px 10px 70px;box-sizing:border-box;}
78
- .grid{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;width:100%;}
79
- .card{
80
- background:#fff;border-radius:10px;overflow:hidden;box-shadow:0 4px 10px rgba(0,0,0,0.08);
81
- height:420px;display:flex;flex-direction:column;position:relative;
82
- }
83
- /* 게임 화면 축소 */
84
- .frame{flex:1;position:relative;overflow:hidden;}
85
- .frame iframe{
86
- position:absolute;top:0;left:0;
87
- width:166.667%;height:166.667%;
88
- transform:scale(0.6);transform-origin:top left;border:0;
89
- }
90
- /* Gradio/Streamlit Embed용 */
91
- .frame.huggingface iframe{
92
- width:100%!important;height:100%!important;
93
- transform:none!important;border:none!important;
94
- }
95
- /* hf.space 전체 페이지용 - 한 번 더 축소 */
96
- .frame.hfspace iframe{
97
- width:200%;height:200%;
98
- transform:scale(0.5);
99
- transform-origin:top left;border:0;
100
- }
101
- /* 하단 바로가기 */
102
- .foot{height:34px;display:flex;align-items:center;justify-content:center;background:#fafafa;border-top:1px solid #eee;}
103
- .foot a{font-size:0.85rem;font-weight:600;color:#4a6dd8;text-decoration:none;}
104
- .foot a:hover{text-decoration:underline;}
105
- /* 반응형 높이 */
106
- @media(min-width:1200px){.card{height:560px;}}
107
- @media(max-width:767px){
108
- .grid{grid-template-columns:1fr;}
109
- .card{height:480px;}
110
- }
111
- </style>"""
112
-
113
- js = """
114
- <script>
115
- /* 허깅페이스 iframe 로딩 오류 처리(생략 - 기존 스크립트 그대로) */
116
- </script>
117
- """
118
-
119
- h = css + js + '<div class="container"><div class="grid">'
120
- for idx, url in enumerate(cards):
121
- iframe_url, extra_cls, alt_urls = process_url_for_iframe(url)
122
- frame_class = f"frame {extra_cls}".strip()
123
- iframe_id = f"iframe-{idx}-{hash(url)%10000}"
124
- alt_attr = f'data-alternate-urls="{",".join(alt_urls)}"' if alt_urls else ""
125
- h += f"""
126
- <div class="card">
127
- <div class="{frame_class}">
128
- <iframe id="{iframe_id}" src="{iframe_url}" loading="lazy"
129
- sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"
130
- data-original-url="{url}" {alt_attr}></iframe>
131
- </div>
132
- <div class="foot"><a href="{url}" target="_blank">↗ Open in Full Screen (New Tab)</a></div>
133
- </div>"""
134
- h += "</div></div>"
135
- h += f'<div class="page-info">Page {pg} / {total}</div>'
136
- return h
137
-
138
-
139
- # ───────────────────── 5. Gradio UI ─────────────────────
140
- def build():
141
- _init_best()
142
-
143
- header = """
144
- <style>
145
- .app-header{position:sticky;top:0;text-align:center;background:#fff;
146
- padding:16px 0 8px;border-bottom:1px solid #eee;z-index:1100;}
147
- .badge-row{display:inline-flex;gap:8px;margin:8px 0;}
148
- </style>
149
- <div class="app-header">
150
- <h1 style="margin:0;font-size:28px;">🎮 Vibe Game Gallery</h1>
151
- <div class="badge-row">
152
- <a href="https://huggingface.co/spaces/openfree/Vibe-Game" target="_blank">
153
- <img src="https://img.shields.io/static/v1?label=huggingface&message=Vibe%20Game%20Craft&color=800080&labelColor=ffa500&logo=huggingface&logoColor=ffff00&style=for-the-badge">
154
- </a>
155
- <a href="https://huggingface.co/spaces/openfree/Game-Gallery" target="_blank">
156
- <img src="https://img.shields.io/static/v1?label=huggingface&message=Game%20Gallery&color=800080&labelColor=ffa500&logo=huggingface&logoColor=ffff00&style=for-the-badge">
157
- </a>
158
- <a href="https://discord.gg/openfreeai" target="_blank">
159
- <img src="https://img.shields.io/static/v1?label=Discord&message=Openfree%20AI&color=0000ff&labelColor=800080&logo=discord&logoColor=white&style=for-the-badge">
160
- </a>
161
- </div>
162
- </div>"""
163
-
164
- global_css = """
165
- footer{display:none !important;}
166
- .button-row{position:fixed;bottom:0;left:0;right:0;height:60px;
167
- background:#f0f0f0;padding:10px;text-align:center;
168
- box-shadow:0 -2px 10px rgba(0,0,0,.05);z-index:1000;}
169
- .button-row button{margin:0 10px;padding:10px 20px;font-size:16px;font-weight:bold;border-radius:50px;}
170
- #content-area{overflow-y:auto;height:calc(100vh - 60px - 120px);}
171
- """
172
-
173
- with gr.Blocks(title="Vibe Game Gallery", css=global_css) as demo:
174
- gr.HTML(header)
175
- out = gr.HTML(elem_id="content-area")
176
- with gr.Row(elem_classes="button-row"):
177
- b_prev = gr.Button("◀ 이전", size="lg")
178
- b_next = gr.Button("다음 ▶", size="lg")
179
-
180
- bp = gr.State(1)
181
-
182
- def render(p=1):
183
- data, tot = page(_load_best(), p)
184
- return html(data, p, tot), p
185
-
186
- b_prev.click(lambda p: render(max(1, p-1)), inputs=bp, outputs=[out, bp])
187
- b_next.click(lambda p: render(p+1), inputs=bp, outputs=[out, bp])
188
-
189
- demo.load(render, outputs=[out, bp])
190
-
191
- return demo
192
-
193
- app = build()
194
 
195
  if __name__ == "__main__":
196
- app.launch()
 
1
+ import os
2
+ import sys
3
+ import streamlit as st
4
+ from tempfile import NamedTemporaryFile
5
 
6
+ def main():
 
 
 
 
 
 
 
 
7
  try:
8
+ # Get the code from secrets
9
+ code = os.environ.get("MAIN_CODE")
10
+
11
+ if not code:
12
+ st.error("⚠️ The application code wasn't found in secrets. Please add the MAIN_CODE secret.")
13
+ return
14
+
15
+ # Create a temporary Python file
16
+ with NamedTemporaryFile(suffix='.py', delete=False, mode='w') as tmp:
17
+ tmp.write(code)
18
+ tmp_path = tmp.name
19
+
20
+ # Execute the code
21
+ exec(compile(code, tmp_path, 'exec'), globals())
22
+
23
+ # Clean up the temporary file
24
+ try:
25
+ os.unlink(tmp_path)
26
+ except:
27
+ pass
28
+
29
  except Exception as e:
30
+ st.error(f"⚠️ Error loading or executing the application: {str(e)}")
31
+ import traceback
32
+ st.code(traceback.format_exc())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
 
34
  if __name__ == "__main__":
35
+ main()