lainlives commited on
Commit
e64e6b4
·
verified ·
1 Parent(s): 58c4d77

Upload folder using huggingface_hub

Browse files
Files changed (2) hide show
  1. README.md +1 -0
  2. app.py +83 -62
README.md CHANGED
@@ -6,5 +6,6 @@ colorTo: red
6
  sdk: gradio
7
  sdk_version: 6.9.0
8
  app_file: app.py
 
9
  pinned: false
10
  ---
 
6
  sdk: gradio
7
  sdk_version: 6.9.0
8
  app_file: app.py
9
+ python_version: 3.12
10
  pinned: false
11
  ---
app.py CHANGED
@@ -7,31 +7,28 @@ import threading
7
  import gradio as gr
8
 
9
  #
 
10
  #
 
 
 
11
  #
12
  #
13
- # This expects a HF_TOKEN and REPOS and RESTART_TIMES env vars
14
- # Expected format is:
15
- # RESTART_TIMES: 06:00,12:00,18:00,00:00
16
- # REPOS: lainlives/bldr,lainlives/ztestzz
17
  #
18
- #
19
- #
20
- #
21
-
22
 
23
  HF_TOKEN = os.getenv("HF_TOKEN")
24
  repos_env = os.getenv("REPOS")
25
  times_env = os.getenv("RESTART_TIMES")
26
 
 
 
 
27
 
28
- REPOS = [t.strip() for t in repos_env.split(",")]
29
- RESTART_TIMES = [t.strip() for t in times_env.split(",")]
30
  HELPER = "lainlives/starter"
31
  SELF_TIME = "00:30"
 
32
  # Global variable to track the specific target timestamp
33
  next_reboot_datetime = None
34
- HELPER_TIME = datetime.strptime(SELF_TIME, "%H:%M")
35
 
36
 
37
  def get_next_scheduled_time():
@@ -39,11 +36,9 @@ def get_next_scheduled_time():
39
  now = datetime.now()
40
  candidates = []
41
 
42
- # Convert all string times to datetime objects for today
43
  for t_str in RESTART_TIMES:
44
  try:
45
  h, m = map(int, t_str.split(':'))
46
- # Create a datetime for today at this time
47
  dt = now.replace(hour=h, minute=m, second=0, microsecond=0)
48
  candidates.append(dt)
49
  except ValueError:
@@ -51,30 +46,47 @@ def get_next_scheduled_time():
51
 
52
  candidates.sort()
53
 
54
- # Find the first time that is in the future
55
  for dt in candidates:
56
  if dt > now:
57
  return dt
58
 
59
- # If no time is left today, pick the earliest time and move it to tomorrow
60
  return candidates[0] + timedelta(days=1)
61
 
62
 
63
- # Initialize the first target time on startup
64
  next_reboot_datetime = get_next_scheduled_time()
65
 
66
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  def time_until_next_reboot():
68
  """Calculates and formats the time until the next planned reboot."""
69
  global next_reboot_datetime
70
-
71
- # Safety check in case it wasn't set
72
  if next_reboot_datetime is None:
73
  next_reboot_datetime = get_next_scheduled_time()
74
 
75
  remaining_time = next_reboot_datetime - datetime.now()
76
 
77
- # If we are past the time (but haven't triggered yet), show 0
78
  if remaining_time.total_seconds() <= 0:
79
  return "Rebooting..."
80
 
@@ -84,80 +96,81 @@ def time_until_next_reboot():
84
 
85
 
86
  def get_last_reboot_status():
87
- """Returns the current target schedule info."""
88
  global next_reboot_datetime
89
  return f"Next target: {next_reboot_datetime.strftime('%Y-%m-%d %H:%M:%S')}"
90
 
91
 
92
- def trigger_helper():
93
- if HF_TOKEN:
94
- api = HfApi()
95
- try:
96
- print(f"Restarting space {HELPER}...")
97
- api.pause_space(repo_id=HELPER)
98
- sleep(3) # Brief pause to ensure state registers
99
- api.restart_space(repo_id=HELPER, token=HF_TOKEN)
100
- except Exception as e:
101
- print(f"Failed to restart {HELPER}: {e}")
102
-
103
-
104
  def check_and_reboot():
105
  """Checks if current time has passed the scheduled time."""
106
  global next_reboot_datetime
107
-
108
  now = datetime.now()
109
 
110
- # Check if we have reached or passed the target time
111
  if now >= next_reboot_datetime:
112
- status_msg = f"Reboot triggered at {now.strftime('%H:%M:%S')}"
113
- print(status_msg)
114
 
115
  # 1. Execute Reboots
116
- if HF_TOKEN:
117
- api = HfApi()
118
- for repo in REPOS:
119
- try:
120
- print(f"Restarting space {repo}...")
121
- api.pause_space(repo_id=repo)
122
- sleep(3) # Brief pause to ensure state registers
123
- api.restart_space(repo_id=repo, token=HF_TOKEN, factory_reboot=True)
124
- except Exception as e:
125
- print(f"Failed to restart {repo}: {e}")
126
-
127
- # 2. IMPORTANT: Advance the schedule immediately to prevent infinite loops
128
- # We calculate the NEXT time starting from 1 minute in the future
129
- # to ensure we don't pick the same time slot we just triggered.
130
  next_reboot_datetime = get_next_scheduled_time()
131
 
132
  return f"Just rebooted. Next scheduled: {next_reboot_datetime.strftime('%H:%M:%S')}"
133
 
134
  return "Space running normally."
135
 
 
136
 
137
- def pipeline():
138
- # Run the check
139
- status = check_and_reboot()
140
- # Return updated UI elements
141
- countdown = time_until_next_reboot()
142
- target_info = get_last_reboot_status()
143
 
144
- return countdown, status, target_info
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
145
 
146
 
147
  def sanitize():
 
148
  now = datetime.now()
149
  target_time = datetime.strptime(SELF_TIME, "%H:%M").time()
150
  next_run = datetime.combine(now.date(), target_time)
151
-
152
- # If time has passed today, schedule for tomorrow
153
  if next_run <= now:
154
  next_run += timedelta(days=1)
155
-
156
  delay = (next_run - now).total_seconds()
157
-
158
  timer = threading.Timer(delay, trigger_helper)
159
  timer.start()
160
- print(f"Scheduled for {next_run}")
 
 
 
 
 
 
 
161
 
162
 
163
  # --- Gradio Interface ---
@@ -170,6 +183,14 @@ with gr.Blocks() as demo:
170
 
171
  status_output = gr.Textbox(label="Log", value="Space running normally.")
172
 
 
 
 
 
 
 
 
 
173
  # Timer ticks every 1 second
174
  timer = gr.Timer(1).tick(
175
  fn=pipeline,
 
7
  import gradio as gr
8
 
9
  #
10
+ # Expects HF_TOKEN, REPOS, and RESTART_TIMES env vars
11
  #
12
+ # API endpoints:
13
+ # /call/status - Returns the current countdown and target time.
14
+ # /call/force_reboot - Immediately triggers the restart of all defined repositories.
15
  #
16
  #
 
 
 
 
17
  #
 
 
 
 
18
 
19
  HF_TOKEN = os.getenv("HF_TOKEN")
20
  repos_env = os.getenv("REPOS")
21
  times_env = os.getenv("RESTART_TIMES")
22
 
23
+ # Fallback for local testing to avoid crashes if envs are missing
24
+ REPOS = [t.strip() for t in repos_env.split(",")] if repos_env else []
25
+ RESTART_TIMES = [t.strip() for t in times_env.split(",")] if times_env else ["00:00"]
26
 
 
 
27
  HELPER = "lainlives/starter"
28
  SELF_TIME = "00:30"
29
+
30
  # Global variable to track the specific target timestamp
31
  next_reboot_datetime = None
 
32
 
33
 
34
  def get_next_scheduled_time():
 
36
  now = datetime.now()
37
  candidates = []
38
 
 
39
  for t_str in RESTART_TIMES:
40
  try:
41
  h, m = map(int, t_str.split(':'))
 
42
  dt = now.replace(hour=h, minute=m, second=0, microsecond=0)
43
  candidates.append(dt)
44
  except ValueError:
 
46
 
47
  candidates.sort()
48
 
 
49
  for dt in candidates:
50
  if dt > now:
51
  return dt
52
 
 
53
  return candidates[0] + timedelta(days=1)
54
 
55
 
56
+ # Initialize first target
57
  next_reboot_datetime = get_next_scheduled_time()
58
 
59
 
60
+ def perform_restarts(source="Manual"):
61
+ """Core logic to restart the repos."""
62
+ log_messages = []
63
+ if HF_TOKEN:
64
+ api = HfApi()
65
+ for repo in REPOS:
66
+ try:
67
+ print(f"[{source}] Restarting space {repo}...")
68
+ api.pause_space(repo_id=repo)
69
+ sleep(3)
70
+ api.restart_space(repo_id=repo, token=HF_TOKEN, factory_reboot=True)
71
+ log_messages.append(f"Successfully restarted {repo}")
72
+ except Exception as e:
73
+ err = f"Failed to restart {repo}: {e}"
74
+ print(err)
75
+ log_messages.append(err)
76
+ else:
77
+ log_messages.append("Error: HF_TOKEN not found.")
78
+
79
+ return "\n".join(log_messages)
80
+
81
+
82
  def time_until_next_reboot():
83
  """Calculates and formats the time until the next planned reboot."""
84
  global next_reboot_datetime
 
 
85
  if next_reboot_datetime is None:
86
  next_reboot_datetime = get_next_scheduled_time()
87
 
88
  remaining_time = next_reboot_datetime - datetime.now()
89
 
 
90
  if remaining_time.total_seconds() <= 0:
91
  return "Rebooting..."
92
 
 
96
 
97
 
98
  def get_last_reboot_status():
 
99
  global next_reboot_datetime
100
  return f"Next target: {next_reboot_datetime.strftime('%Y-%m-%d %H:%M:%S')}"
101
 
102
 
 
 
 
 
 
 
 
 
 
 
 
 
103
  def check_and_reboot():
104
  """Checks if current time has passed the scheduled time."""
105
  global next_reboot_datetime
 
106
  now = datetime.now()
107
 
 
108
  if now >= next_reboot_datetime:
109
+ print(f"Reboot triggered at {now.strftime('%H:%M:%S')}")
 
110
 
111
  # 1. Execute Reboots
112
+ perform_restarts(source="Schedule")
113
+
114
+ # 2. Advance schedule
 
 
 
 
 
 
 
 
 
 
 
115
  next_reboot_datetime = get_next_scheduled_time()
116
 
117
  return f"Just rebooted. Next scheduled: {next_reboot_datetime.strftime('%H:%M:%S')}"
118
 
119
  return "Space running normally."
120
 
121
+ # --- API Functions ---
122
 
 
 
 
 
 
 
123
 
124
+ def api_get_info():
125
+ """Returns JSON status for the API."""
126
+ return {
127
+ "status": "active",
128
+ "next_reboot": str(next_reboot_datetime),
129
+ "time_remaining": time_until_next_reboot(),
130
+ "monitored_repos": REPOS
131
+ }
132
+
133
+
134
+ def api_trigger_now():
135
+ """Triggers immediate reboot via API."""
136
+ logs = perform_restarts(source="API Force")
137
+ return {
138
+ "status": "executed",
139
+ "timestamp": str(datetime.now()),
140
+ "logs": logs
141
+ }
142
+
143
+
144
+ def trigger_helper():
145
+ if HF_TOKEN:
146
+ api = HfApi()
147
+ try:
148
+ print(f"Restarting space {HELPER}...")
149
+ api.pause_space(repo_id=HELPER)
150
+ sleep(3)
151
+ api.restart_space(repo_id=HELPER, token=HF_TOKEN)
152
+ except Exception as e:
153
+ print(f"Failed to restart {HELPER}: {e}")
154
 
155
 
156
  def sanitize():
157
+ # Sanitizer logic remains the same
158
  now = datetime.now()
159
  target_time = datetime.strptime(SELF_TIME, "%H:%M").time()
160
  next_run = datetime.combine(now.date(), target_time)
 
 
161
  if next_run <= now:
162
  next_run += timedelta(days=1)
 
163
  delay = (next_run - now).total_seconds()
 
164
  timer = threading.Timer(delay, trigger_helper)
165
  timer.start()
166
+ print(f"Scheduled helper restart for {next_run}")
167
+
168
+
169
+ def pipeline():
170
+ status = check_and_reboot()
171
+ countdown = time_until_next_reboot()
172
+ target_info = get_last_reboot_status()
173
+ return countdown, status, target_info
174
 
175
 
176
  # --- Gradio Interface ---
 
183
 
184
  status_output = gr.Textbox(label="Log", value="Space running normally.")
185
 
186
+ # --- Hidden API Elements ---
187
+ # These buttons are hidden but define the API endpoints via 'api_name'
188
+ api_info_btn = gr.Button("Get Info", visible=False)
189
+ api_info_btn.click(fn=api_get_info, outputs=gr.JSON(), api_name="status")
190
+
191
+ api_force_btn = gr.Button("Force Reboot", visible=False)
192
+ api_force_btn.click(fn=api_trigger_now, outputs=gr.JSON(), api_name="force_reboot")
193
+
194
  # Timer ticks every 1 second
195
  timer = gr.Timer(1).tick(
196
  fn=pipeline,