AIencoder commited on
Commit
4df2067
Β·
verified Β·
1 Parent(s): e3d3329

[deploy] app.py

Browse files
Files changed (1) hide show
  1. app.py +153 -0
app.py ADDED
@@ -0,0 +1,153 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ πŸ“‘ Space Pinger
3
+ ================
4
+ Pings all AIencoder HuggingFace Spaces every 4 minutes to keep them alive.
5
+ Auto-discovers spaces via HF API so it stays up to date.
6
+ """
7
+
8
+ import gradio as gr
9
+ import requests, threading, time, os
10
+ from datetime import datetime
11
+ from huggingface_hub import HfApi
12
+
13
+ HF_TOKEN = os.environ.get("HF_TOKEN", "")
14
+ OWNER = "AIencoder"
15
+ INTERVAL = 240 # 4 minutes (HF sleeps after 5 min of inactivity)
16
+
17
+ state = {
18
+ "running": False,
19
+ "log": "Ready. Press Start to begin pinging.\n",
20
+ "spaces": [],
21
+ "ping_count": 0,
22
+ "last_round": "Never",
23
+ }
24
+ lock = threading.Lock()
25
+
26
+
27
+ def log(msg):
28
+ ts = datetime.now().strftime("%H:%M:%S")
29
+ with lock:
30
+ state["log"] += f"[{ts}] {msg}\n"
31
+ lines = state["log"].split("\n")
32
+ if len(lines) > 300:
33
+ state["log"] = "\n".join(lines[-300:])
34
+
35
+
36
+ def get_spaces():
37
+ """Fetch all spaces for the owner."""
38
+ try:
39
+ api = HfApi(token=HF_TOKEN)
40
+ spaces = list(api.list_spaces(author=OWNER, token=HF_TOKEN))
41
+ urls = []
42
+ for s in spaces:
43
+ space_id = s.id
44
+ # Skip self to avoid circular pinging
45
+ if space_id == f"{OWNER}/space-pinger":
46
+ continue
47
+ # Convert to URL: owner/space-name -> https://owner-space-name.hf.space
48
+ slug = space_id.replace("/", "-").replace("_", "-").lower()
49
+ url = f"https://{slug}.hf.space"
50
+ urls.append((space_id, url))
51
+ return urls
52
+ except Exception as e:
53
+ log(f"⚠️ Could not fetch spaces: {e}")
54
+ return state["spaces"]
55
+
56
+
57
+ def ping_all():
58
+ """Ping every space once."""
59
+ spaces = get_spaces()
60
+ state["spaces"] = spaces
61
+ log(f"\nπŸ”„ Pinging {len(spaces)} spaces...")
62
+
63
+ ok, fail = 0, 0
64
+ for space_id, url in spaces:
65
+ try:
66
+ r = requests.get(url, timeout=15, allow_redirects=True)
67
+ status = r.status_code
68
+ if status < 400:
69
+ log(f" βœ… {space_id} ({status})")
70
+ ok += 1
71
+ else:
72
+ log(f" ⚠️ {space_id} ({status})")
73
+ fail += 1
74
+ except Exception as e:
75
+ log(f" ❌ {space_id} β€” {type(e).__name__}")
76
+ fail += 1
77
+
78
+ state["ping_count"] += 1
79
+ state["last_round"] = datetime.now().strftime("%H:%M:%S")
80
+ log(f"Round {state['ping_count']} done β€” βœ… {ok} up, ❌ {fail} down. Next in {INTERVAL//60} min.")
81
+
82
+
83
+ def ping_loop():
84
+ """Background loop."""
85
+ while state["running"]:
86
+ ping_all()
87
+ # Sleep in small chunks so stop signal is responsive
88
+ for _ in range(INTERVAL):
89
+ if not state["running"]:
90
+ break
91
+ time.sleep(1)
92
+ log("β›” Pinger stopped.")
93
+
94
+
95
+ def start():
96
+ if state["running"]:
97
+ return "⚠️ Already running!"
98
+ state["running"] = True
99
+ state["log"] = "πŸš€ Pinger starting...\n"
100
+ threading.Thread(target=ping_loop, daemon=True).start()
101
+ return "βœ… Pinger started! Pinging every 4 minutes."
102
+
103
+
104
+ def stop():
105
+ state["running"] = False
106
+ return "β›” Stop signal sent."
107
+
108
+
109
+ def get_log():
110
+ with lock:
111
+ return state["log"]
112
+
113
+
114
+ def get_status():
115
+ spaces = state["spaces"]
116
+ status = "🟒 Running" if state["running"] else "⚫ Idle"
117
+ space_list = "\n".join(f"- {sid}" for sid, _ in spaces) if spaces else "_Not fetched yet_"
118
+ return (
119
+ f"**Status:** {status} \n"
120
+ f"**Ping rounds completed:** {state['ping_count']} \n"
121
+ f"**Last round:** {state['last_round']} \n"
122
+ f"**Interval:** {INTERVAL//60} minutes \n\n"
123
+ f"**Spaces being pinged ({len(spaces)}):**\n{space_list}"
124
+ )
125
+
126
+
127
+ with gr.Blocks(title="πŸ“‘ Space Pinger", theme=gr.themes.Soft()) as app:
128
+ gr.Markdown("# πŸ“‘ Space Pinger\nKeeps all AIencoder Spaces alive by pinging every 4 minutes.")
129
+
130
+ with gr.Row():
131
+ start_btn = gr.Button("πŸš€ Start Pinger", variant="primary", scale=2)
132
+ stop_btn = gr.Button("β›” Stop", variant="stop", scale=1)
133
+
134
+ status_out = gr.Markdown("*Press Start.*")
135
+
136
+ with gr.Row():
137
+ with gr.Column(scale=1):
138
+ gr.Markdown("### πŸ“Š Status")
139
+ status_box = gr.Markdown("*No data yet.*")
140
+ gr.Button("πŸ”„ Refresh").click(get_status, outputs=status_box)
141
+ with gr.Column(scale=2):
142
+ gr.Markdown("### πŸ“‹ Log")
143
+ log_box = gr.Textbox(lines=20, max_lines=20, interactive=False)
144
+ gr.Button("πŸ”„ Refresh Log").click(get_log, outputs=log_box)
145
+
146
+ start_btn.click(start, outputs=status_out)
147
+ stop_btn.click(stop, outputs=status_out)
148
+
149
+ timer = gr.Timer(value=10)
150
+ timer.tick(get_log, outputs=log_box)
151
+ timer.tick(get_status, outputs=status_box)
152
+
153
+ app.launch(server_name="0.0.0.0", server_port=7860)