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