luigi12345 commited on
Commit
71006c1
β€’
1 Parent(s): d7a57be

Create streamlit-app.py

Browse files
Files changed (1) hide show
  1. streamlit-app.py +336 -0
streamlit-app.py ADDED
@@ -0,0 +1,336 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # InfiniteStorageFace.py
2
+ # Required Libraries:
3
+ # pip install streamlit huggingface_hub
4
+
5
+ import os
6
+ import streamlit as st
7
+ from huggingface_hub import HfApi, create_repo, upload_file, login
8
+ from threading import Thread, Event, Lock
9
+ import time
10
+ import re
11
+
12
+ # ----------------------------
13
+ # Set Page Configuration
14
+ # ----------------------------
15
+ st.set_page_config(page_title="πŸš€ InfiniteStorageFace", layout="wide")
16
+
17
+ # ----------------------------
18
+ # Configuration and Constants
19
+ # ----------------------------
20
+
21
+ # Regular expression for validating repository ID
22
+ REPO_ID_REGEX = re.compile(r"^[a-zA-Z0-9\-_.]+/[a-zA-Z0-9\-_.]+$")
23
+
24
+ # Prefilled sample values (placeholders)
25
+ SAMPLE_TOKEN = "" # Leave empty for security; user must input
26
+ SAMPLE_FOLDER_PATH = "/Users/samihalawa/Documents/Megacursos/MEGACURSOS_S3_MASTER/angular"
27
+ SAMPLE_REPO_ID = "luigi12345/megacursos1"
28
+ SAMPLE_THREADS = 5
29
+
30
+ # ----------------------------
31
+ # Global Variables and Locks
32
+ # ----------------------------
33
+
34
+ # Event to signal upload cancellation
35
+ cancel_event = Event()
36
+
37
+ # Lock for thread-safe operations
38
+ upload_lock = Lock()
39
+
40
+ # Shared log list (in-memory cache)
41
+ if 'logs' not in st.session_state:
42
+ st.session_state.logs = []
43
+ if 'uploading' not in st.session_state:
44
+ st.session_state.uploading = False
45
+
46
+ # ----------------------------
47
+ # Helper Functions
48
+ # ----------------------------
49
+
50
+ def log(message):
51
+ """Logs a message by adding it to the shared_logs list."""
52
+ with upload_lock:
53
+ st.session_state.logs.append(message)
54
+
55
+ def authenticate(token):
56
+ """Authenticates the user with Hugging Face using the provided token."""
57
+ if not token:
58
+ log("❌ Hugging Face Token is required.")
59
+ return False, "❌ Hugging Face Token is required."
60
+ try:
61
+ login(token) # Authenticate user
62
+ log("βœ… Authenticated successfully!")
63
+ return True, "βœ… Authenticated successfully!"
64
+ except Exception as e:
65
+ log(f"❌ Authentication failed: {e}")
66
+ return False, f"❌ Authentication failed: {e}"
67
+
68
+ def validate_repo_id(repo_id):
69
+ """Validates the format of the repository ID."""
70
+ if not repo_id:
71
+ log("❌ Repository ID is required.")
72
+ return False, "❌ Repository ID is required."
73
+ if not REPO_ID_REGEX.match(repo_id):
74
+ log("❌ Repository ID must be in the format 'username/repo-name'.")
75
+ return False, "❌ Repository ID must be in the format 'username/repo-name'."
76
+ return True, "βœ… Repository ID format is valid."
77
+
78
+ def create_repo_if_not_exists(repo_id, token, repo_type="space", private=False):
79
+ """Creates a repository if it does not exist."""
80
+ api = HfApi()
81
+ try:
82
+ # Check if the repository exists by listing its files
83
+ api.list_repo_files(repo_id=repo_id, repo_type=repo_type, token=token)
84
+ log(f"βœ… Repository '{repo_id}' exists. Proceeding with upload...")
85
+ return True, f"βœ… Repository '{repo_id}' exists. Proceeding with upload..."
86
+ except Exception:
87
+ # If repository does not exist, create it
88
+ try:
89
+ create_repo(repo_id=repo_id, token=token, private=private, repo_type=repo_type, exist_ok=True)
90
+ log(f"βœ… Created new repository: '{repo_id}'.")
91
+ return True, f"βœ… Created new repository: '{repo_id}'."
92
+ except Exception as create_err:
93
+ log(f"❌ Failed to create repository '{repo_id}': {create_err}")
94
+ return False, f"❌ Failed to create repository '{repo_id}': {create_err}"
95
+
96
+ def upload_files(folder_path, repo_id, token, private=False, threads=5, subfolder=None):
97
+ """Handles the uploading of files to the Hugging Face repository."""
98
+ if cancel_event.is_set():
99
+ log("❌ Upload has been cancelled.")
100
+ return "❌ Upload has been cancelled."
101
+
102
+ # Validate inputs
103
+ if not folder_path:
104
+ log("❌ Folder Path is required.")
105
+ return "❌ Folder Path is required."
106
+
107
+ # Normalize the folder path
108
+ folder_path = os.path.normpath(folder_path).strip()
109
+
110
+ if not os.path.isabs(folder_path):
111
+ log("❌ Please provide an absolute folder path.")
112
+ return "❌ Please provide an absolute folder path."
113
+
114
+ if not os.path.isdir(folder_path):
115
+ log(f"❌ The folder path '{folder_path}' does not exist.")
116
+ return f"❌ The folder path '{folder_path}' does not exist."
117
+
118
+ if not repo_id:
119
+ log("❌ Repository ID is required.")
120
+ return "❌ Repository ID is required."
121
+
122
+ valid, message = validate_repo_id(repo_id)
123
+ if not valid:
124
+ return message
125
+
126
+ if not token:
127
+ log("❌ Hugging Face Token is required.")
128
+ return "❌ Hugging Face Token is required."
129
+
130
+ # Check if the folder contains files
131
+ if not any(os.scandir(folder_path)):
132
+ log("❌ The folder is empty. No files to upload.")
133
+ return "❌ The folder is empty. No files to upload."
134
+
135
+ def upload_process():
136
+ with upload_lock:
137
+ st.session_state.uploading = True
138
+ try:
139
+ # Step 1: Authenticate
140
+ success, auth_message = authenticate(token)
141
+ if not success:
142
+ st.session_state.uploading = False
143
+ return
144
+
145
+ # Step 2: Create repository if it doesn't exist
146
+ success, creation_message = create_repo_if_not_exists(repo_id, token, repo_type="space", private=private)
147
+ if not success:
148
+ st.session_state.uploading = False
149
+ return
150
+
151
+ # Step 3: Start upload
152
+ log("πŸš€ Starting upload process...")
153
+ start_time = time.time()
154
+
155
+ # Iterate through all files in the folder
156
+ total_files = sum(len(files) for _, _, files in os.walk(folder_path))
157
+ uploaded_files = 0
158
+
159
+ for root, dirs, files in os.walk(folder_path):
160
+ for file in files:
161
+ if cancel_event.is_set():
162
+ log("⚠️ Upload cancelled by user.")
163
+ st.session_state.uploading = False
164
+ return
165
+ file_path = os.path.join(root, file)
166
+ relative_path = os.path.relpath(file_path, folder_path)
167
+ repo_path = os.path.join(subfolder, relative_path) if subfolder else relative_path
168
+ try:
169
+ upload_file(
170
+ path_or_fileobj=file_path,
171
+ path_in_repo=repo_path,
172
+ repo_id=repo_id,
173
+ token=token,
174
+ repo_type="space",
175
+ commit_message=f"Add {relative_path}"
176
+ )
177
+ uploaded_files += 1
178
+ log(f"βœ… Uploaded: {relative_path} ({uploaded_files}/{total_files})")
179
+ except Exception as e:
180
+ log(f"❌ Failed to upload {relative_path}: {e}")
181
+
182
+ elapsed_time = time.time() - start_time
183
+ log(f"βœ… Upload completed successfully in {int(elapsed_time)} seconds!")
184
+ except Exception as e:
185
+ log(f"❌ An unexpected error occurred during upload: {e}")
186
+ finally:
187
+ st.session_state.uploading = False
188
+
189
+ # Start the upload process in a separate thread to keep the UI responsive
190
+ upload_thread = Thread(target=upload_process, daemon=True)
191
+ upload_thread.start()
192
+
193
+ return "πŸš€ Upload initiated. Check the logs for progress."
194
+
195
+ def display_logs(log_container):
196
+ """Displays the logs from st.session_state.logs."""
197
+ with log_container.container():
198
+ if st.session_state.logs:
199
+ # Join all log messages with newlines
200
+ log_text = "\n".join(st.session_state.logs)
201
+ # Display in a read-only text area with auto-scroll
202
+ st.text_area("πŸ“œ Upload Logs", log_text, height=400, key='log_text', disabled=True)
203
+ else:
204
+ st.text_area("πŸ“œ Upload Logs", "Logs will appear here...", height=400, key='log_text', disabled=True)
205
+
206
+ def render_tree(tree, indent=0, max_depth=3, current_depth=0):
207
+ """Recursively render the folder structure, limiting depth to simplify display."""
208
+ if current_depth >= max_depth:
209
+ return
210
+ for key, value in sorted(tree.items()):
211
+ if value is None:
212
+ st.markdown(" " * indent + f"- {key}")
213
+ else:
214
+ st.markdown(" " * indent + f"- **{key}/**")
215
+ render_tree(value, indent + 2, max_depth, current_depth + 1)
216
+
217
+ # ----------------------------
218
+ # Main Application
219
+ # ----------------------------
220
+
221
+ def main():
222
+ # Auto-refresh every 1 second to update logs
223
+ # Note: This approach can be heavy on resources. Use with caution.
224
+ st_autorefresh = getattr(st, "autorefresh", None)
225
+ if st_autorefresh:
226
+ st.autorefresh(interval=1000, limit=1000, key="log_refresh")
227
+
228
+ st.title("πŸš€ InfiniteStorageFace")
229
+ st.markdown("**Effortlessly upload your files to Hugging Face Spaces with real-time feedback and progress tracking!**")
230
+
231
+ # Sidebar for configuration
232
+ with st.sidebar:
233
+ st.header("πŸ“‹ Configuration")
234
+
235
+ token = st.text_input(
236
+ "Hugging Face Token",
237
+ type="password",
238
+ placeholder="Enter your Hugging Face API token",
239
+ value=SAMPLE_TOKEN,
240
+ )
241
+
242
+ private = st.checkbox(
243
+ "Make Repository Private",
244
+ value=False,
245
+ )
246
+
247
+ folder_path = st.text_input(
248
+ "Folder Path to Upload",
249
+ placeholder="Enter the absolute path to your folder",
250
+ value=SAMPLE_FOLDER_PATH,
251
+ )
252
+
253
+ repo_id = st.text_input(
254
+ "Repository ID",
255
+ placeholder="e.g., username/repo-name",
256
+ value=SAMPLE_REPO_ID,
257
+ )
258
+
259
+ subfolder = st.text_input(
260
+ "Subfolder in Repository (optional)",
261
+ placeholder="e.g., data/uploads",
262
+ value="",
263
+ )
264
+
265
+ threads = st.slider(
266
+ "Number of Threads",
267
+ min_value=1,
268
+ max_value=20,
269
+ value=SAMPLE_THREADS,
270
+ help="Adjust the number of threads to optimize upload speed based on your internet connection.",
271
+ )
272
+
273
+ upload_button = st.button("Start Upload")
274
+ cancel_button = st.button("Cancel Upload")
275
+
276
+ # Main area for logs and folder structure
277
+ col1, col2 = st.columns([2, 1])
278
+
279
+ with col1:
280
+ st.subheader("πŸ“œ Upload Logs")
281
+ log_container = st.empty()
282
+ display_logs(log_container)
283
+ if st.button("Clear Logs"):
284
+ with upload_lock:
285
+ st.session_state.logs = []
286
+ log_container.empty()
287
+
288
+ with col2:
289
+ st.subheader("πŸ“ Folder Structure")
290
+ if os.path.isabs(folder_path) and os.path.isdir(folder_path):
291
+ tree = {}
292
+ for root, dirs, files in os.walk(folder_path):
293
+ sub_dir = root.replace(folder_path, "").strip(os.sep)
294
+ parent = tree
295
+ if sub_dir:
296
+ for part in sub_dir.split(os.sep):
297
+ parent = parent.setdefault(part, {})
298
+ for d in dirs:
299
+ parent[d] = {}
300
+ for f in files:
301
+ parent[f] = None
302
+
303
+ render_tree(tree)
304
+ else:
305
+ st.warning("Please enter a valid absolute folder path to display its structure.")
306
+
307
+ # Handle upload and cancellation
308
+ if upload_button and not st.session_state.uploading:
309
+ # Clear previous logs
310
+ with upload_lock:
311
+ st.session_state.logs = []
312
+ cancel_event.clear()
313
+ status = upload_files(folder_path, repo_id, token, private, threads, subfolder)
314
+ log(status)
315
+ elif upload_button and st.session_state.uploading:
316
+ st.warning("πŸš€ Upload is already in progress.")
317
+
318
+ if cancel_button and st.session_state.uploading:
319
+ cancel_event.set()
320
+ log("⚠️ Upload cancellation requested.")
321
+
322
+ st.markdown("""
323
+ ---
324
+ **πŸ“Œ Instructions**:
325
+ 1. **Hugging Face Token**: Obtain your API token from your [Hugging Face account settings](https://huggingface.co/settings/tokens).
326
+ 2. **Folder Path**: Specify the absolute path to the folder you want to upload.
327
+ 3. **Repository ID**: Enter the desired repository name in the format `username/repo-name`.
328
+ 4. **Subfolder in Repository**: Optionally, specify a subfolder in the repository to upload your files into.
329
+ 5. **Number of Threads**: Adjust the number of threads to optimize upload speed based on your internet connection.
330
+ 6. **Start Upload**: Click to begin uploading your files.
331
+ 7. **Cancel Upload**: Click to cancel an ongoing upload process.
332
+ 8. **Clear Logs**: Click to clear the upload logs.
333
+ """)
334
+
335
+ if __name__ == "__main__":
336
+ main()