lainlives commited on
Commit
316aa0c
·
verified ·
1 Parent(s): 32f69db

Upload folder using huggingface_hub

Browse files
Files changed (2) hide show
  1. README.md +0 -2
  2. app.py +158 -0
README.md CHANGED
@@ -8,5 +8,3 @@ sdk_version: 6.9.0
8
  app_file: app.py
9
  pinned: false
10
  ---
11
-
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
8
  app_file: app.py
9
  pinned: false
10
  ---
 
 
app.py ADDED
@@ -0,0 +1,158 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import os
3
+ from time import sleep
4
+ from huggingface_hub import HfApi
5
+ from datetime import datetime, timedelta
6
+
7
+
8
+ #
9
+ #
10
+ #
11
+ #
12
+ # This expects a HF_TOKEN and REPOS and RESTART_TIMES env vars
13
+ # Expected format is:
14
+ # RESTART_TIMES: 06:00,12:00,18:00,00:00
15
+ # REPOS: lainlives/bldr,lainlives/starter
16
+ #
17
+ #
18
+ #
19
+ #
20
+
21
+
22
+ # --- Configuration ---
23
+ # Get Space ID and Token from environment variables (Secrets)
24
+ HF_TOKEN = os.getenv("HF_TOKEN")
25
+ # REPOS = ["lainlives/bldr", "lainlives/starter"]
26
+ repos_env = os.getenv("REPOS")
27
+ # List of times to restart (24-hour format, UTC usually)
28
+ # RESTART_TIMES = ["06:00", "12:00", "18:00", "00:00"]
29
+
30
+
31
+ times_env = os.getenv("RESTART_TIMES")
32
+ REPOS = [t.strip() for t in repos_env.split(",")]
33
+ RESTART_TIMES = [t.strip() for t in times_env.split(",")]
34
+
35
+
36
+ # Global variable to track the specific target timestamp
37
+ next_reboot_datetime = None
38
+
39
+
40
+ def get_next_scheduled_time():
41
+ """Finds the next scheduled time from the list relative to now."""
42
+ now = datetime.now()
43
+ candidates = []
44
+
45
+ # Convert all string times to datetime objects for today
46
+ for t_str in RESTART_TIMES:
47
+ try:
48
+ h, m = map(int, t_str.split(':'))
49
+ # Create a datetime for today at this time
50
+ dt = now.replace(hour=h, minute=m, second=0, microsecond=0)
51
+ candidates.append(dt)
52
+ except ValueError:
53
+ print(f"Invalid time format: {t_str}")
54
+
55
+ candidates.sort()
56
+
57
+ # Find the first time that is in the future
58
+ for dt in candidates:
59
+ if dt > now:
60
+ return dt
61
+
62
+ # If no time is left today, pick the earliest time and move it to tomorrow
63
+ return candidates[0] + timedelta(days=1)
64
+
65
+
66
+ # Initialize the first target time on startup
67
+ next_reboot_datetime = get_next_scheduled_time()
68
+
69
+
70
+ def time_until_next_reboot():
71
+ """Calculates and formats the time until the next planned reboot."""
72
+ global next_reboot_datetime
73
+
74
+ # Safety check in case it wasn't set
75
+ if next_reboot_datetime is None:
76
+ next_reboot_datetime = get_next_scheduled_time()
77
+
78
+ remaining_time = next_reboot_datetime - datetime.now()
79
+
80
+ # If we are past the time (but haven't triggered yet), show 0
81
+ if remaining_time.total_seconds() <= 0:
82
+ return "Rebooting..."
83
+
84
+ mins, secs = divmod(remaining_time.seconds, 60)
85
+ hours, mins = divmod(mins, 60)
86
+ return f"{hours:02}:{mins:02}:{secs:02}"
87
+
88
+
89
+ def get_last_reboot_status():
90
+ """Returns the current target schedule info."""
91
+ global next_reboot_datetime
92
+ return f"Next target: {next_reboot_datetime.strftime('%Y-%m-%d %H:%M:%S')}"
93
+
94
+
95
+ def check_and_reboot():
96
+ """Checks if current time has passed the scheduled time."""
97
+ global next_reboot_datetime
98
+
99
+ now = datetime.now()
100
+
101
+ # Check if we have reached or passed the target time
102
+ if now >= next_reboot_datetime:
103
+ status_msg = f"Reboot triggered at {now.strftime('%H:%M:%S')}"
104
+ print(status_msg)
105
+
106
+ # 1. Execute Reboots
107
+ if HF_TOKEN:
108
+ api = HfApi()
109
+ for repo in REPOS:
110
+ try:
111
+ print(f"Restarting space {repo}...")
112
+ api.pause_space(repo_id=repo)
113
+ sleep(1) # Brief pause to ensure state registers
114
+ api.restart_space(repo_id=repo, token=HF_TOKEN)
115
+ except Exception as e:
116
+ print(f"Failed to restart {repo}: {e}")
117
+
118
+ # 2. IMPORTANT: Advance the schedule immediately to prevent infinite loops
119
+ # We calculate the NEXT time starting from 1 minute in the future
120
+ # to ensure we don't pick the same time slot we just triggered.
121
+ next_reboot_datetime = get_next_scheduled_time()
122
+
123
+ return f"Just rebooted. Next scheduled: {next_reboot_datetime.strftime('%H:%M:%S')}"
124
+
125
+ return "Space running normally."
126
+
127
+
128
+ def pipeline():
129
+ # Run the check
130
+ status = check_and_reboot()
131
+
132
+ # Return updated UI elements
133
+ countdown = time_until_next_reboot()
134
+ target_info = get_last_reboot_status()
135
+
136
+ return countdown, status, target_info
137
+
138
+
139
+ # --- Gradio Interface ---
140
+ with gr.Blocks() as demo:
141
+ gr.Markdown("## Space Rebooter")
142
+
143
+ with gr.Row():
144
+ next_reboot_display = gr.Textbox(label="Scheduled Target", value=get_last_reboot_status())
145
+ next_reboot_countdown = gr.Textbox(label="Time Until Next Reboot", value=time_until_next_reboot())
146
+
147
+ status_output = gr.Textbox(label="Status Log", value="Space running normally.")
148
+
149
+ # Timer ticks every 1 second
150
+ timer = gr.Timer(interval=1).tick(
151
+ fn=pipeline,
152
+ inputs=None,
153
+ outputs=[next_reboot_countdown, status_output, next_reboot_display],
154
+ every=1
155
+ )
156
+
157
+ if __name__ == "__main__":
158
+ demo.launch(server_name="0.0.0.0", server_port=7860)