Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -1,13 +1,15 @@
|
|
|
|
1 |
import os, shutil, zipfile, threading, time
|
2 |
from flask import Flask, request, render_template_string
|
3 |
import gdown
|
4 |
from huggingface_hub import HfApi, login, upload_folder
|
5 |
|
6 |
-
# Environment variables (set
|
7 |
FOLDER_URL = os.getenv("FOLDER_URL")
|
8 |
REPO_ID = os.getenv("REPO_ID")
|
9 |
TOKEN = os.getenv("HF_TOKEN")
|
10 |
|
|
|
11 |
DOWNLOAD_DIR = "/tmp/backups"
|
12 |
EXTRACT_DIR = "/tmp/extracted_backups"
|
13 |
|
@@ -17,95 +19,106 @@ schedule_interval = 0
|
|
17 |
|
18 |
app = Flask(__name__)
|
19 |
|
20 |
-
# Backup logic
|
21 |
def run_backup():
|
22 |
global last_backup_time
|
|
|
23 |
try:
|
|
|
|
|
24 |
shutil.rmtree(DOWNLOAD_DIR, ignore_errors=True)
|
25 |
shutil.rmtree(EXTRACT_DIR, ignore_errors=True)
|
26 |
os.makedirs(DOWNLOAD_DIR, exist_ok=True)
|
27 |
os.makedirs(EXTRACT_DIR, exist_ok=True)
|
|
|
28 |
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
quiet=False
|
34 |
-
)
|
35 |
|
|
|
36 |
for root, _, files in os.walk(DOWNLOAD_DIR):
|
37 |
for f in files:
|
38 |
if f.endswith(".zip"):
|
39 |
zp = os.path.join(root, f)
|
40 |
with zipfile.ZipFile(zp) as z:
|
41 |
z.extractall(EXTRACT_DIR)
|
|
|
42 |
|
43 |
-
|
|
|
44 |
good = os.path.join(EXTRACT_DIR, "world_nether")
|
45 |
if os.path.exists(bad) and not os.path.exists(good):
|
46 |
os.rename(bad, good)
|
|
|
47 |
|
|
|
48 |
login(token=TOKEN)
|
49 |
api = HfApi()
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
api.create_repo(repo_id=REPO_ID, repo_type="dataset", private=False, exist_ok=True)
|
56 |
|
|
|
57 |
subfolders = {
|
58 |
"world": os.path.join(EXTRACT_DIR, "world"),
|
59 |
"world_nether": os.path.join(EXTRACT_DIR, "world_nether"),
|
60 |
"world_the_end": os.path.join(EXTRACT_DIR, "world_the_end"),
|
61 |
"plugins": os.path.join(EXTRACT_DIR, "plugins")
|
62 |
}
|
63 |
-
|
64 |
for name, path in subfolders.items():
|
65 |
if os.path.exists(path):
|
|
|
66 |
upload_folder(
|
67 |
repo_id=REPO_ID,
|
68 |
folder_path=path,
|
69 |
repo_type="dataset",
|
70 |
token=TOKEN,
|
71 |
path_in_repo=name,
|
72 |
-
commit_message="add "
|
73 |
)
|
|
|
|
|
|
|
|
|
74 |
last_backup_time = time.ctime()
|
75 |
-
|
76 |
except Exception as e:
|
77 |
-
|
|
|
78 |
|
79 |
-
# Background
|
80 |
def schedule_loop():
|
81 |
while True:
|
82 |
if schedule_interval > 0:
|
83 |
run_backup()
|
84 |
time.sleep(schedule_interval * 60)
|
85 |
else:
|
86 |
-
time.sleep(
|
87 |
|
88 |
-
# Start scheduler thread
|
89 |
threading.Thread(target=schedule_loop, daemon=True).start()
|
90 |
|
91 |
-
# HTML template
|
92 |
-
HTML =
|
93 |
<!DOCTYPE html>
|
94 |
<html>
|
95 |
<head>
|
96 |
-
<
|
97 |
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
|
98 |
<style>
|
99 |
body { font-family: sans-serif; padding: 20px; max-width: 600px; margin: auto; }
|
100 |
h2 { font-size: 24px; }
|
101 |
-
input, button { width: 100%; padding:
|
102 |
-
.box { background: #f5f5f5; padding: 15px; border-radius: 8px; margin-top: 20px; }
|
103 |
</style>
|
104 |
</head>
|
105 |
<body>
|
106 |
<h2>Minecraft Backup Controller</h2>
|
107 |
<form method="post">
|
108 |
-
<label>Set interval (minutes)
|
109 |
<input type="number" name="interval" value="{{ interval }}" min="1">
|
110 |
<button type="submit">Set Timer</button>
|
111 |
</form>
|
@@ -115,18 +128,18 @@ HTML = """
|
|
115 |
</form>
|
116 |
<div class="box">
|
117 |
<p><strong>Last Backup:</strong> {{ last_run }}</p>
|
118 |
-
<p><strong>Status:</strong>
|
119 |
</div>
|
120 |
</body>
|
121 |
</html>
|
122 |
-
|
123 |
|
124 |
@app.route("/", methods=["GET", "POST"])
|
125 |
def index():
|
126 |
global schedule_interval
|
127 |
status = ""
|
128 |
if request.method == "POST":
|
129 |
-
if "manual_run"
|
130 |
status = run_backup()
|
131 |
else:
|
132 |
try:
|
@@ -138,3 +151,11 @@ def index():
|
|
138 |
|
139 |
if __name__ == "__main__":
|
140 |
app.run(host="0.0.0.0", port=7860)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# app.py
|
2 |
import os, shutil, zipfile, threading, time
|
3 |
from flask import Flask, request, render_template_string
|
4 |
import gdown
|
5 |
from huggingface_hub import HfApi, login, upload_folder
|
6 |
|
7 |
+
# Environment variables (set these in your Space settings)
|
8 |
FOLDER_URL = os.getenv("FOLDER_URL")
|
9 |
REPO_ID = os.getenv("REPO_ID")
|
10 |
TOKEN = os.getenv("HF_TOKEN")
|
11 |
|
12 |
+
# Directories in writable tmp
|
13 |
DOWNLOAD_DIR = "/tmp/backups"
|
14 |
EXTRACT_DIR = "/tmp/extracted_backups"
|
15 |
|
|
|
19 |
|
20 |
app = Flask(__name__)
|
21 |
|
22 |
+
# Backup logic with detailed logging
|
23 |
def run_backup():
|
24 |
global last_backup_time
|
25 |
+
log_entries = []
|
26 |
try:
|
27 |
+
log_entries.append("Starting backup process...")
|
28 |
+
# Clean previous
|
29 |
shutil.rmtree(DOWNLOAD_DIR, ignore_errors=True)
|
30 |
shutil.rmtree(EXTRACT_DIR, ignore_errors=True)
|
31 |
os.makedirs(DOWNLOAD_DIR, exist_ok=True)
|
32 |
os.makedirs(EXTRACT_DIR, exist_ok=True)
|
33 |
+
log_entries.append(f"Directories reset: {DOWNLOAD_DIR}, {EXTRACT_DIR}")
|
34 |
|
35 |
+
# Download from Google Drive
|
36 |
+
log_entries.append(f"Downloading from: {FOLDER_URL}")
|
37 |
+
gdown.download_folder(url=FOLDER_URL, output=DOWNLOAD_DIR, use_cookies=False, quiet=True)
|
38 |
+
log_entries.append("Download complete.")
|
|
|
|
|
39 |
|
40 |
+
# Extract all zip files
|
41 |
for root, _, files in os.walk(DOWNLOAD_DIR):
|
42 |
for f in files:
|
43 |
if f.endswith(".zip"):
|
44 |
zp = os.path.join(root, f)
|
45 |
with zipfile.ZipFile(zp) as z:
|
46 |
z.extractall(EXTRACT_DIR)
|
47 |
+
log_entries.append(f"Extracted: {zp}")
|
48 |
|
49 |
+
# Fix potential typo folder
|
50 |
+
bad = os.path.join(EXTRACT_DIR, "world_nither")
|
51 |
good = os.path.join(EXTRACT_DIR, "world_nether")
|
52 |
if os.path.exists(bad) and not os.path.exists(good):
|
53 |
os.rename(bad, good)
|
54 |
+
log_entries.append(f"Renamed folder: {bad} -> {good}")
|
55 |
|
56 |
+
# Authenticate and prepare dataset
|
57 |
login(token=TOKEN)
|
58 |
api = HfApi()
|
59 |
+
log_entries.append("Authenticated to Hugging Face.")
|
60 |
+
# Ensure dataset exists
|
61 |
+
api.create_repo(repo_id=REPO_ID, repo_type="dataset", private=False, exist_ok=True, token=TOKEN)
|
62 |
+
log_entries.append(f"Dataset ensured: {REPO_ID}")
|
|
|
|
|
63 |
|
64 |
+
# Upload subfolders
|
65 |
subfolders = {
|
66 |
"world": os.path.join(EXTRACT_DIR, "world"),
|
67 |
"world_nether": os.path.join(EXTRACT_DIR, "world_nether"),
|
68 |
"world_the_end": os.path.join(EXTRACT_DIR, "world_the_end"),
|
69 |
"plugins": os.path.join(EXTRACT_DIR, "plugins")
|
70 |
}
|
|
|
71 |
for name, path in subfolders.items():
|
72 |
if os.path.exists(path):
|
73 |
+
log_entries.append(f"Uploading folder: {name}")
|
74 |
upload_folder(
|
75 |
repo_id=REPO_ID,
|
76 |
folder_path=path,
|
77 |
repo_type="dataset",
|
78 |
token=TOKEN,
|
79 |
path_in_repo=name,
|
80 |
+
commit_message=f"add {name}"
|
81 |
)
|
82 |
+
log_entries.append(f"Uploaded: {name}")
|
83 |
+
else:
|
84 |
+
log_entries.append(f"Skipped missing folder: {path}")
|
85 |
+
|
86 |
last_backup_time = time.ctime()
|
87 |
+
log_entries.append(f"Backup completed at {last_backup_time}")
|
88 |
except Exception as e:
|
89 |
+
log_entries.append(f"Error: {str(e)}")
|
90 |
+
return "<br>".join(log_entries)
|
91 |
|
92 |
+
# Background scheduler
|
93 |
def schedule_loop():
|
94 |
while True:
|
95 |
if schedule_interval > 0:
|
96 |
run_backup()
|
97 |
time.sleep(schedule_interval * 60)
|
98 |
else:
|
99 |
+
time.sleep(5)
|
100 |
|
|
|
101 |
threading.Thread(target=schedule_loop, daemon=True).start()
|
102 |
|
103 |
+
# HTML UI template
|
104 |
+
HTML = '''
|
105 |
<!DOCTYPE html>
|
106 |
<html>
|
107 |
<head>
|
108 |
+
<meta charset="utf-8">
|
109 |
<meta name="viewport" content="width=device-width, initial-scale=1">
|
110 |
+
<title>Minecraft Backup Panel</title>
|
111 |
<style>
|
112 |
body { font-family: sans-serif; padding: 20px; max-width: 600px; margin: auto; }
|
113 |
h2 { font-size: 24px; }
|
114 |
+
input, button { width: 100%; padding: 12px; margin: 8px 0; font-size: 16px; border-radius: 6px; border: 1px solid #ccc; }
|
115 |
+
.box { background: #f5f5f5; padding: 15px; border-radius: 8px; margin-top: 20px; word-wrap: break-word; }
|
116 |
</style>
|
117 |
</head>
|
118 |
<body>
|
119 |
<h2>Minecraft Backup Controller</h2>
|
120 |
<form method="post">
|
121 |
+
<label>Set interval (minutes):</label>
|
122 |
<input type="number" name="interval" value="{{ interval }}" min="1">
|
123 |
<button type="submit">Set Timer</button>
|
124 |
</form>
|
|
|
128 |
</form>
|
129 |
<div class="box">
|
130 |
<p><strong>Last Backup:</strong> {{ last_run }}</p>
|
131 |
+
<p><strong>Status Log:</strong><br>{{ status|safe }}</p>
|
132 |
</div>
|
133 |
</body>
|
134 |
</html>
|
135 |
+
'''
|
136 |
|
137 |
@app.route("/", methods=["GET", "POST"])
|
138 |
def index():
|
139 |
global schedule_interval
|
140 |
status = ""
|
141 |
if request.method == "POST":
|
142 |
+
if request.form.get("manual_run"):
|
143 |
status = run_backup()
|
144 |
else:
|
145 |
try:
|
|
|
151 |
|
152 |
if __name__ == "__main__":
|
153 |
app.run(host="0.0.0.0", port=7860)
|
154 |
+
|
155 |
+
# Dockerfile
|
156 |
+
# ----------
|
157 |
+
# FROM python:3.10
|
158 |
+
# WORKDIR /app
|
159 |
+
# COPY app.py .
|
160 |
+
# RUN pip install flask huggingface_hub gdown
|
161 |
+
# CMD ["python", "app.py"]
|