Alamgirapi commited on
Commit
c5c1ddb
·
verified ·
1 Parent(s): 23d4453

Delete huggingface-uploader

Browse files
huggingface-uploader/Dockerfile DELETED
@@ -1,25 +0,0 @@
1
- FROM python:3.9-slim
2
-
3
- WORKDIR /app
4
-
5
- # Install system dependencies
6
- RUN apt-get update && apt-get install -y \
7
- build-essential \
8
- && rm -rf /var/lib/apt/lists/*
9
-
10
- # Copy requirements and install Python dependencies
11
- COPY requirements.txt .
12
- RUN pip install --no-cache-dir -r requirements.txt
13
-
14
- # Copy application files
15
- COPY app.py .
16
- COPY templates/ templates/
17
-
18
- # Create uploads directory
19
- RUN mkdir -p uploads
20
-
21
- # Expose port
22
- EXPOSE 7860
23
-
24
- # Run the application
25
- CMD ["gunicorn", "--bind", "0.0.0.0:7860", "--timeout", "300", "--workers", "1", "app:app"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
huggingface-uploader/README.md DELETED
@@ -1,154 +0,0 @@
1
- # HuggingFace Uploader
2
-
3
- A Flask web application for uploading files and folders to Hugging Face Spaces, Models, and Datasets.
4
-
5
- ## Features
6
-
7
- - 🌐 **Web-based Interface**: Clean, modern UI with drag-and-drop support
8
- - 📁 **ZIP Support**: Upload entire folders by compressing them to ZIP
9
- - 🚀 **Multiple Repository Types**: Support for Spaces, Models, and Datasets
10
- - 📊 **Progress Tracking**: Real-time upload progress monitoring
11
- - 🔄 **Background Processing**: Non-blocking uploads with threading
12
- - 🧹 **Auto Cleanup**: Automatic cleanup of temporary files
13
- - 📱 **Responsive Design**: Works on desktop and mobile devices
14
-
15
- ## Installation
16
-
17
- 1. **Clone or download the files**
18
- 2. **Install dependencies**:
19
- ```bash
20
- pip install -r requirements.txt
21
- ```
22
-
23
- ## Directory Structure
24
-
25
- ```
26
- huggingface-uploader/
27
- ├── app.py # Main Flask application
28
- ├── requirements.txt # Python dependencies
29
- ├── README.md # This file
30
- ├── templates/
31
- │ └── index.html # Web interface template
32
- └── uploads/ # Temporary upload directory (auto-created)
33
- ```
34
-
35
- ## Usage
36
-
37
- 1. **Start the application**:
38
- ```bash
39
- python app.py
40
- ```
41
-
42
- 2. **Open your browser** and navigate to `http://localhost:5000`
43
-
44
- 3. **Fill in the form**:
45
- - **Hugging Face Token**: Get from [HuggingFace Settings](https://huggingface.co/settings/tokens)
46
- - **Repository ID**: Format as `username/repository-name`
47
- - **Repository Type**: Choose between Space, Model, or Dataset
48
- - **Commit Message**: Describe your upload
49
- - **File**: Select a file or ZIP folder
50
-
51
- 4. **Upload**: Click "Upload to HuggingFace" and monitor progress
52
-
53
- ## Supported File Types
54
-
55
- - **Archives**: ZIP
56
- - **Code**: Python (.py), JavaScript (.js), HTML (.html), CSS (.css), JSON (.json)
57
- - **Documents**: Text (.txt), Markdown (.md), PDF (.pdf)
58
- - **Images**: PNG, JPG, JPEG, GIF
59
-
60
- ## Configuration
61
-
62
- ### Environment Variables
63
-
64
- You can customize the application by modifying these variables in `app.py`:
65
-
66
- ```python
67
- UPLOAD_FOLDER = 'uploads' # Temporary upload directory
68
- MAX_CONTENT_LENGTH = 500 * 1024 * 1024 # Max file size (500MB)
69
- SECRET_KEY = 'your-secret-key-change-this' # Flask secret key
70
- ```
71
-
72
- ### Security
73
-
74
- - Change the `SECRET_KEY` in production
75
- - Consider adding authentication for production use
76
- - Implement rate limiting for public deployments
77
-
78
- ## API Endpoints
79
-
80
- - `GET /` - Main upload interface
81
- - `POST /upload` - Handle file uploads
82
- - `GET /progress/<upload_id>` - Get upload progress
83
- - `GET /cleanup` - Manual cleanup of old files
84
-
85
- ## Error Handling
86
-
87
- The application includes comprehensive error handling for:
88
- - Invalid file types
89
- - Missing required fields
90
- - HuggingFace API errors
91
- - Network connectivity issues
92
- - File size limitations
93
-
94
- ## Auto-Cleanup
95
-
96
- The application automatically cleans up temporary files older than 1 hour. You can also trigger manual cleanup by visiting `/cleanup`.
97
-
98
- ## Development
99
-
100
- To run in development mode:
101
- ```bash
102
- python app.py
103
- ```
104
-
105
- The application runs on `http://localhost:5000` with debug mode enabled.
106
-
107
- ## Deployment
108
-
109
- For production deployment:
110
-
111
- 1. Set `debug=False` in `app.py`
112
- 2. Change the `SECRET_KEY` to a secure random value
113
- 3. Configure a proper web server (e.g., nginx + gunicorn)
114
- 4. Set up proper file permissions
115
- 5. Consider implementing authentication
116
-
117
- ## Troubleshooting
118
-
119
- ### Common Issues
120
-
121
- 1. **"No module named 'huggingface_hub'"**
122
- - Run: `pip install -r requirements.txt`
123
-
124
- 2. **"Permission denied" errors**
125
- - Ensure the application has write permissions to the uploads directory
126
-
127
- 3. **Upload fails with authentication error**
128
- - Verify your HuggingFace token is correct and has proper permissions
129
-
130
- 4. **File too large**
131
- - Check if your file exceeds the 500MB limit
132
-
133
- ### Logs
134
-
135
- The application logs errors to the console. Check the terminal output for detailed error messages.
136
-
137
- ## Contributing
138
-
139
- 1. Fork the repository
140
- 2. Create a feature branch
141
- 3. Make your changes
142
- 4. Test thoroughly
143
- 5. Submit a pull request
144
-
145
- ## License
146
-
147
- This project is open source and available under the MIT License.
148
-
149
- ## Support
150
-
151
- For support, please:
152
- 1. Check the troubleshooting section above
153
- 2. Review the HuggingFace documentation
154
- 3. Open an issue on the project repository
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
huggingface-uploader/app.py DELETED
@@ -1,276 +0,0 @@
1
- import os
2
- import shutil
3
- import tempfile
4
- import zipfile
5
- from datetime import datetime
6
- from flask import Flask, render_template, request, jsonify, flash, redirect, url_for
7
- from werkzeug.utils import secure_filename
8
- from huggingface_hub import HfApi, upload_folder
9
- import threading
10
- import time
11
- import logging
12
- import traceback
13
-
14
- # Configure logging
15
- logging.basicConfig(level=logging.INFO)
16
- logger = logging.getLogger(__name__)
17
-
18
- app = Flask(__name__)
19
- app.secret_key = os.environ.get('SECRET_KEY', 'your-secret-key-change-this')
20
-
21
- # Configuration
22
- UPLOAD_FOLDER = 'uploads'
23
- ALLOWED_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif', 'py', 'js', 'html', 'css', 'json', 'md', 'zip', 'yml', 'yaml', 'toml', 'cfg', 'ini', 'conf', 'log', 'xml', 'svg', 'ico', 'woff', 'woff2', 'ttf', 'eot', 'mp4', 'mp3', 'wav', 'avi', 'mov', 'webm', 'webp', 'tiff', 'bmp'}
24
- # Common files without extensions
25
- ALLOWED_NO_EXTENSION_FILES = {'dockerfile', 'makefile', 'readme', 'license', 'changelog', 'authors', 'contributors', 'copying', 'install', 'news', 'todo', 'requirements', 'pipfile', 'procfile', 'cmakelists', 'gitignore', 'gitattributes', 'dockerignore', 'editorconfig', 'flake8', 'pylintrc', 'pre-commit-config', 'travis', 'appveyor', 'circle', 'azure-pipelines'}
26
-
27
- MAX_CONTENT_LENGTH = 500 * 1024 * 1024 # 500MB max file size
28
-
29
- app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
30
- app.config['MAX_CONTENT_LENGTH'] = MAX_CONTENT_LENGTH
31
-
32
- # Ensure upload directory exists
33
- os.makedirs(UPLOAD_FOLDER, exist_ok=True)
34
-
35
- # Global variables for tracking upload progress
36
- upload_progress = {}
37
- upload_status = {}
38
-
39
- def allowed_file(filename):
40
- """Check if file is allowed based on extension or known files without extensions"""
41
- if '.' in filename:
42
- extension = filename.rsplit('.', 1)[1].lower()
43
- return extension in ALLOWED_EXTENSIONS
44
- else:
45
- # Check if it's a known file without extension
46
- return filename.lower() in ALLOWED_NO_EXTENSION_FILES
47
-
48
- def extract_zip(zip_path, extract_to):
49
- """Extract ZIP file to specified directory"""
50
- with zipfile.ZipFile(zip_path, 'r') as zip_ref:
51
- zip_ref.extractall(extract_to)
52
-
53
- def create_zip_from_files(files, zip_path):
54
- """Create a ZIP file from uploaded files"""
55
- with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
56
- for file in files:
57
- if file.filename:
58
- # Save file temporarily
59
- temp_path = os.path.join(tempfile.gettempdir(), secure_filename(file.filename))
60
- file.save(temp_path)
61
-
62
- # Add to ZIP with the original filename
63
- zipf.write(temp_path, file.filename)
64
-
65
- # Clean up temp file
66
- os.remove(temp_path)
67
-
68
- def upload_to_huggingface(upload_id, folder_path, repo_id, hf_token, commit_message, repo_type="space"):
69
- """Upload folder to Hugging Face in a separate thread"""
70
- try:
71
- logger.info(f"Starting upload {upload_id} to {repo_id}")
72
- upload_status[upload_id] = "Initializing..."
73
- upload_progress[upload_id] = 0
74
-
75
- # Initialize API
76
- logger.info(f"Initializing HfApi for {upload_id}")
77
- api = HfApi(token=hf_token)
78
-
79
- # Test token validity
80
- try:
81
- logger.info(f"Testing token validity for {upload_id}")
82
- user_info = api.whoami()
83
- logger.info(f"Token valid for user: {user_info.get('name', 'Unknown')}")
84
- except Exception as e:
85
- logger.error(f"Token validation failed for {upload_id}: {str(e)}")
86
- upload_status[upload_id] = f"Invalid token: {str(e)}"
87
- return
88
-
89
- upload_status[upload_id] = "Uploading to Hugging Face..."
90
- upload_progress[upload_id] = 25
91
-
92
- logger.info(f"Starting upload for {upload_id}: {folder_path} -> {repo_id}")
93
-
94
- # Always upload as folder to maintain directory structure
95
- if os.path.isfile(folder_path):
96
- logger.info(f"Converting single file to folder structure: {folder_path}")
97
- # Create a temporary folder for single file
98
- temp_folder = os.path.join(os.path.dirname(folder_path), f"temp_folder_{upload_id}")
99
- os.makedirs(temp_folder, exist_ok=True)
100
-
101
- # Copy the file to the temporary folder with original name
102
- filename = os.path.basename(folder_path)
103
- temp_file_path = os.path.join(temp_folder, filename)
104
- shutil.copy2(folder_path, temp_file_path)
105
-
106
- # Upload the temporary folder
107
- upload_folder(
108
- folder_path=temp_folder,
109
- repo_id=repo_id,
110
- repo_type=repo_type,
111
- token=hf_token,
112
- commit_message=commit_message,
113
- ignore_patterns=[".git", "__pycache__", "*.pyc", ".env", "*.log"]
114
- )
115
-
116
- # Clean up temporary folder
117
- shutil.rmtree(temp_folder)
118
-
119
- else:
120
- logger.info(f"Uploading folder: {folder_path}")
121
- # Folder upload
122
- upload_folder(
123
- folder_path=folder_path,
124
- repo_id=repo_id,
125
- repo_type=repo_type,
126
- token=hf_token,
127
- commit_message=commit_message,
128
- ignore_patterns=[".git", "__pycache__", "*.pyc", ".env", "*.log"]
129
- )
130
-
131
- upload_progress[upload_id] = 100
132
- upload_status[upload_id] = "Upload completed successfully!"
133
- logger.info(f"Upload {upload_id} completed successfully")
134
-
135
- except Exception as e:
136
- error_msg = f"Upload error: {str(e)}"
137
- logger.error(f"Upload {upload_id} failed: {error_msg}")
138
- logger.error(f"Full traceback: {traceback.format_exc()}")
139
- upload_status[upload_id] = error_msg
140
- upload_progress[upload_id] = 0
141
-
142
- @app.route('/')
143
- def index():
144
- return render_template('index.html')
145
-
146
- @app.route('/upload', methods=['POST'])
147
- def upload_file():
148
- try:
149
- # Get form data
150
- hf_token = request.form.get('hf_token')
151
- repo_id = request.form.get('repo_id')
152
- repo_type = request.form.get('repo_type', 'space')
153
- commit_message = request.form.get('commit_message', 'Upload via Flask App')
154
-
155
- logger.info(f"Upload request: repo_id={repo_id}, repo_type={repo_type}")
156
-
157
- if not hf_token or not repo_id:
158
- return jsonify({'error': 'Hugging Face token and repository ID are required'}), 400
159
-
160
- # Check if files were uploaded
161
- if 'files' not in request.files:
162
- return jsonify({'error': 'No files selected'}), 400
163
-
164
- files = request.files.getlist('files')
165
- if not files or all(file.filename == '' for file in files):
166
- return jsonify({'error': 'No files selected'}), 400
167
-
168
- # Generate unique upload ID and timestamp for internal tracking
169
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
170
- upload_id = f"upload_{timestamp}"
171
-
172
- # Handle multiple files and folder structures
173
- if len(files) == 1 and not any('/' in file.filename for file in files):
174
- # Single file upload
175
- file = files[0]
176
- if file and allowed_file(file.filename):
177
- # Use original filename without timestamp
178
- filename = secure_filename(file.filename)
179
-
180
- # Create unique folder for this upload to avoid conflicts
181
- upload_folder_path = os.path.join(app.config['UPLOAD_FOLDER'], f"upload_{timestamp}")
182
- os.makedirs(upload_folder_path, exist_ok=True)
183
-
184
- # Save uploaded file with original name
185
- file_path = os.path.join(upload_folder_path, filename)
186
- file.save(file_path)
187
- logger.info(f"File saved: {file_path}")
188
-
189
- # Handle ZIP files
190
- if filename.lower().endswith('.zip'):
191
- extract_folder = os.path.join(upload_folder_path, "extracted")
192
- os.makedirs(extract_folder, exist_ok=True)
193
- extract_zip(file_path, extract_folder)
194
- upload_path = extract_folder
195
- logger.info(f"ZIP extracted to: {extract_folder}")
196
- else:
197
- upload_path = file_path
198
- else:
199
- return jsonify({'error': f'File type not allowed: {file.filename}'}), 400
200
- else:
201
- # Multiple files or folder structure - create folder structure
202
- folder_path = os.path.join(app.config['UPLOAD_FOLDER'], f"folder_{timestamp}")
203
- os.makedirs(folder_path, exist_ok=True)
204
-
205
- # Save all files to the folder, preserving directory structure
206
- for file in files:
207
- if file and file.filename and allowed_file(file.filename):
208
- # Use the original filename which may include directory structure
209
- filename = file.filename
210
- # Ensure the filename is safe but preserve directory structure
211
- safe_filename = filename.replace('\\', '/').strip('/')
212
- file_path = os.path.join(folder_path, safe_filename)
213
-
214
- # Create subdirectories if needed (for folder uploads)
215
- os.makedirs(os.path.dirname(file_path), exist_ok=True)
216
- file.save(file_path)
217
- logger.info(f"File saved: {file_path}")
218
- elif file and file.filename:
219
- return jsonify({'error': f'File type not allowed: {file.filename}'}), 400
220
-
221
- upload_path = folder_path
222
- logger.info(f"Folder created: {folder_path}")
223
-
224
- # Start upload in background thread
225
- thread = threading.Thread(
226
- target=upload_to_huggingface,
227
- args=(upload_id, upload_path, repo_id, hf_token, commit_message, repo_type)
228
- )
229
- thread.daemon = True
230
- thread.start()
231
-
232
- return jsonify({
233
- 'success': True,
234
- 'upload_id': upload_id,
235
- 'message': 'Upload started successfully!'
236
- })
237
-
238
- except Exception as e:
239
- logger.error(f"Upload endpoint error: {str(e)}")
240
- logger.error(f"Full traceback: {traceback.format_exc()}")
241
- return jsonify({'error': str(e)}), 500
242
-
243
- @app.route('/progress/<upload_id>')
244
- def get_progress(upload_id):
245
- """Get upload progress"""
246
- progress = upload_progress.get(upload_id, 0)
247
- status = upload_status.get(upload_id, "Unknown")
248
-
249
- return jsonify({
250
- 'progress': progress,
251
- 'status': status,
252
- 'completed': progress == 100 and 'Error' not in status
253
- })
254
-
255
- @app.route('/cleanup')
256
- def cleanup():
257
- """Clean up old uploaded files"""
258
- try:
259
- # Remove files older than 1 hour
260
- cutoff_time = time.time() - 3600 # 1 hour
261
-
262
- for filename in os.listdir(app.config['UPLOAD_FOLDER']):
263
- file_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
264
- if os.path.getctime(file_path) < cutoff_time:
265
- if os.path.isfile(file_path):
266
- os.remove(file_path)
267
- elif os.path.isdir(file_path):
268
- shutil.rmtree(file_path)
269
-
270
- return jsonify({'message': 'Cleanup completed'})
271
- except Exception as e:
272
- return jsonify({'error': str(e)}), 500
273
-
274
- if __name__ == '__main__':
275
- port = int(os.environ.get('PORT', 7860)) # Hugging Face Spaces uses port 7860
276
- app.run(debug=False, host='0.0.0.0', port=port, use_reloader=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
huggingface-uploader/app1.py DELETED
@@ -1,263 +0,0 @@
1
- import os
2
- import shutil
3
- import tempfile
4
- import zipfile
5
- from datetime import datetime
6
- from flask import Flask, render_template, request, jsonify, flash, redirect, url_for
7
- from werkzeug.utils import secure_filename
8
- from huggingface_hub import HfApi, upload_folder
9
- import threading
10
- import time
11
- import logging
12
- import traceback
13
-
14
- # Configure logging
15
- logging.basicConfig(level=logging.INFO)
16
- logger = logging.getLogger(__name__)
17
-
18
- app = Flask(__name__)
19
- app.secret_key = 'your-secret-key-change-this' # Change this in production
20
-
21
- # Configuration
22
- UPLOAD_FOLDER = 'uploads'
23
- ALLOWED_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif', 'py', 'js', 'html', 'css', 'json', 'md', 'zip'}
24
- MAX_CONTENT_LENGTH = 500 * 1024 * 1024 # 500MB max file size
25
-
26
- app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
27
- app.config['MAX_CONTENT_LENGTH'] = MAX_CONTENT_LENGTH
28
-
29
- # Ensure upload directory exists
30
- os.makedirs(UPLOAD_FOLDER, exist_ok=True)
31
-
32
- # Global variables for tracking upload progress
33
- upload_progress = {}
34
- upload_status = {}
35
-
36
- def allowed_file(filename):
37
- return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
38
-
39
- def extract_zip(zip_path, extract_to):
40
- """Extract ZIP file to specified directory"""
41
- with zipfile.ZipFile(zip_path, 'r') as zip_ref:
42
- zip_ref.extractall(extract_to)
43
-
44
- def create_zip_from_files(files, zip_path):
45
- """Create a ZIP file from uploaded files"""
46
- with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
47
- for file in files:
48
- if file.filename:
49
- # Save file temporarily
50
- temp_path = os.path.join(tempfile.gettempdir(), secure_filename(file.filename))
51
- file.save(temp_path)
52
-
53
- # Add to ZIP with the original filename
54
- zipf.write(temp_path, file.filename)
55
-
56
- # Clean up temp file
57
- os.remove(temp_path)
58
-
59
- def upload_to_huggingface(upload_id, folder_path, repo_id, hf_token, commit_message, repo_type="space"):
60
- """Upload folder to Hugging Face in a separate thread"""
61
- try:
62
- logger.info(f"Starting upload {upload_id} to {repo_id}")
63
- upload_status[upload_id] = "Initializing..."
64
- upload_progress[upload_id] = 0
65
-
66
- # Initialize API
67
- logger.info(f"Initializing HfApi for {upload_id}")
68
- api = HfApi(token=hf_token)
69
-
70
- # Test token validity
71
- try:
72
- logger.info(f"Testing token validity for {upload_id}")
73
- user_info = api.whoami()
74
- logger.info(f"Token valid for user: {user_info.get('name', 'Unknown')}")
75
- except Exception as e:
76
- logger.error(f"Token validation failed for {upload_id}: {str(e)}")
77
- upload_status[upload_id] = f"Invalid token: {str(e)}"
78
- return
79
-
80
- upload_status[upload_id] = "Uploading to Hugging Face..."
81
- upload_progress[upload_id] = 25
82
-
83
- logger.info(f"Starting upload for {upload_id}: {folder_path} -> {repo_id}")
84
-
85
- # Always upload as folder to maintain directory structure
86
- if os.path.isfile(folder_path):
87
- logger.info(f"Converting single file to folder structure: {folder_path}")
88
- # Create a temporary folder for single file
89
- temp_folder = os.path.join(os.path.dirname(folder_path), f"temp_folder_{upload_id}")
90
- os.makedirs(temp_folder, exist_ok=True)
91
-
92
- # Copy the file to the temporary folder
93
- filename = os.path.basename(folder_path)
94
- temp_file_path = os.path.join(temp_folder, filename)
95
- shutil.copy2(folder_path, temp_file_path)
96
-
97
- # Upload the temporary folder
98
- upload_folder(
99
- folder_path=temp_folder,
100
- repo_id=repo_id,
101
- repo_type=repo_type,
102
- token=hf_token,
103
- commit_message=commit_message,
104
- ignore_patterns=[".git", "__pycache__", "*.pyc", ".env", "*.log"]
105
- )
106
-
107
- # Clean up temporary folder
108
- shutil.rmtree(temp_folder)
109
-
110
- else:
111
- logger.info(f"Uploading folder: {folder_path}")
112
- # Folder upload
113
- upload_folder(
114
- folder_path=folder_path,
115
- repo_id=repo_id,
116
- repo_type=repo_type,
117
- token=hf_token,
118
- commit_message=commit_message,
119
- ignore_patterns=[".git", "__pycache__", "*.pyc", ".env", "*.log"]
120
- )
121
-
122
- upload_progress[upload_id] = 100
123
- upload_status[upload_id] = "Upload completed successfully!"
124
- logger.info(f"Upload {upload_id} completed successfully")
125
-
126
- except Exception as e:
127
- error_msg = f"Upload error: {str(e)}"
128
- logger.error(f"Upload {upload_id} failed: {error_msg}")
129
- logger.error(f"Full traceback: {traceback.format_exc()}")
130
- upload_status[upload_id] = error_msg
131
- upload_progress[upload_id] = 0
132
-
133
- @app.route('/')
134
- def index():
135
- return render_template('index.html')
136
-
137
- @app.route('/upload', methods=['POST'])
138
- def upload_file():
139
- try:
140
- # Get form data
141
- hf_token = request.form.get('hf_token')
142
- repo_id = request.form.get('repo_id')
143
- repo_type = request.form.get('repo_type', 'space')
144
- commit_message = request.form.get('commit_message', 'Upload via Flask App')
145
-
146
- logger.info(f"Upload request: repo_id={repo_id}, repo_type={repo_type}")
147
-
148
- if not hf_token or not repo_id:
149
- return jsonify({'error': 'Hugging Face token and repository ID are required'}), 400
150
-
151
- # Check if files were uploaded
152
- if 'files' not in request.files:
153
- return jsonify({'error': 'No files selected'}), 400
154
-
155
- files = request.files.getlist('files')
156
- if not files or all(file.filename == '' for file in files):
157
- return jsonify({'error': 'No files selected'}), 400
158
-
159
- # Generate unique upload ID and timestamp
160
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
161
- upload_id = f"upload_{timestamp}"
162
-
163
- # Handle multiple files and folder structures
164
- if len(files) == 1 and not any('/' in file.filename for file in files):
165
- # Single file upload
166
- file = files[0]
167
- if file and allowed_file(file.filename):
168
- filename = secure_filename(file.filename)
169
- unique_filename = f"{timestamp}_{filename}"
170
-
171
- # Save uploaded file
172
- file_path = os.path.join(app.config['UPLOAD_FOLDER'], unique_filename)
173
- file.save(file_path)
174
- logger.info(f"File saved: {file_path}")
175
-
176
- # Handle ZIP files
177
- if filename.lower().endswith('.zip'):
178
- extract_folder = os.path.join(app.config['UPLOAD_FOLDER'], f"extracted_{timestamp}")
179
- os.makedirs(extract_folder, exist_ok=True)
180
- extract_zip(file_path, extract_folder)
181
- upload_path = extract_folder
182
- logger.info(f"ZIP extracted to: {extract_folder}")
183
- else:
184
- upload_path = file_path
185
- else:
186
- return jsonify({'error': 'File type not allowed'}), 400
187
- else:
188
- # Multiple files or folder structure - create folder structure
189
- folder_path = os.path.join(app.config['UPLOAD_FOLDER'], f"folder_{timestamp}")
190
- os.makedirs(folder_path, exist_ok=True)
191
-
192
- # Save all files to the folder, preserving directory structure
193
- for file in files:
194
- if file and file.filename and allowed_file(file.filename):
195
- # Use the original filename which may include directory structure
196
- filename = file.filename
197
- # Ensure the filename is safe but preserve directory structure
198
- safe_filename = filename.replace('\\', '/').strip('/')
199
- file_path = os.path.join(folder_path, safe_filename)
200
-
201
- # Create subdirectories if needed (for folder uploads)
202
- os.makedirs(os.path.dirname(file_path), exist_ok=True)
203
- file.save(file_path)
204
- logger.info(f"File saved: {file_path}")
205
- elif file and file.filename:
206
- return jsonify({'error': f'File type not allowed: {file.filename}'}), 400
207
-
208
- upload_path = folder_path
209
- logger.info(f"Folder created: {folder_path}")
210
-
211
- # Start upload in background thread
212
- thread = threading.Thread(
213
- target=upload_to_huggingface,
214
- args=(upload_id, upload_path, repo_id, hf_token, commit_message, repo_type)
215
- )
216
- thread.daemon = True
217
- thread.start()
218
-
219
- return jsonify({
220
- 'success': True,
221
- 'upload_id': upload_id,
222
- 'message': 'Upload started successfully!'
223
- })
224
-
225
- except Exception as e:
226
- logger.error(f"Upload endpoint error: {str(e)}")
227
- logger.error(f"Full traceback: {traceback.format_exc()}")
228
- return jsonify({'error': str(e)}), 500
229
-
230
- @app.route('/progress/<upload_id>')
231
- def get_progress(upload_id):
232
- """Get upload progress"""
233
- progress = upload_progress.get(upload_id, 0)
234
- status = upload_status.get(upload_id, "Unknown")
235
-
236
- return jsonify({
237
- 'progress': progress,
238
- 'status': status,
239
- 'completed': progress == 100 and 'Error' not in status
240
- })
241
-
242
- @app.route('/cleanup')
243
- def cleanup():
244
- """Clean up old uploaded files"""
245
- try:
246
- # Remove files older than 1 hour
247
- cutoff_time = time.time() - 3600 # 1 hour
248
-
249
- for filename in os.listdir(app.config['UPLOAD_FOLDER']):
250
- file_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
251
- if os.path.getctime(file_path) < cutoff_time:
252
- if os.path.isfile(file_path):
253
- os.remove(file_path)
254
- elif os.path.isdir(file_path):
255
- shutil.rmtree(file_path)
256
-
257
- return jsonify({'message': 'Cleanup completed'})
258
- except Exception as e:
259
- return jsonify({'error': str(e)}), 500
260
-
261
- if __name__ == '__main__':
262
- # Disable auto-reloader to prevent background thread interruption
263
- app.run(debug=True, host='0.0.0.0', port=5000, use_reloader=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
huggingface-uploader/app2.py DELETED
@@ -1,328 +0,0 @@
1
- import os
2
- import shutil
3
- import tempfile
4
- import zipfile
5
- from datetime import datetime
6
- from flask import Flask, render_template, request, jsonify, flash, redirect, url_for
7
- from werkzeug.utils import secure_filename
8
- from huggingface_hub import HfApi, upload_folder
9
- import threading
10
- import time
11
- import logging
12
- import traceback
13
-
14
- # Configure logging
15
- logging.basicConfig(level=logging.INFO)
16
- logger = logging.getLogger(__name__)
17
-
18
- app = Flask(__name__)
19
- app.secret_key = 'your-secret-key-change-this' # Change this in production
20
-
21
- # Configuration
22
- UPLOAD_FOLDER = 'uploads'
23
- ALLOWED_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif', 'py', 'js', 'html', 'css', 'json', 'md', 'zip', 'tar', 'gz', 'tgz', 'ipynb', 'xlsx', 'docx', 'csv', 'xml', 'yaml', 'yml', 'toml', 'ini', 'cfg', 'log', 'sql', 'sh', 'bat', 'ps1', 'r', 'cpp', 'c', 'java', 'php', 'rb', 'go', 'rs', 'swift', 'kt', 'scala', 'pl', 'lua', 'dart', 'vue', 'tsx', 'ts', 'jsx', 'sass', 'scss', 'less', 'styl', 'woff', 'woff2', 'ttf', 'otf', 'eot', 'svg', 'ico', 'webp', 'mp4', 'webm', 'ogg', 'mp3', 'wav', 'flac', 'aac', 'pkl', 'h5', 'hdf5', 'npy', 'npz', 'pt', 'pth', 'onnx', 'pb', 'tflite', 'safetensors', 'bin', 'model', 'weights', 'ckpt', 'checkpoint'}
24
- MAX_CONTENT_LENGTH = 1024 * 1024 * 1024 # 1GB max file size
25
-
26
- app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
27
- app.config['MAX_CONTENT_LENGTH'] = MAX_CONTENT_LENGTH
28
-
29
- # Ensure upload directory exists
30
- os.makedirs(UPLOAD_FOLDER, exist_ok=True)
31
-
32
- # Global variables for tracking upload progress
33
- upload_progress = {}
34
- upload_status = {}
35
- upload_history = []
36
-
37
- def allowed_file(filename):
38
- return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
39
-
40
- def extract_zip(zip_path, extract_to):
41
- """Extract ZIP file to specified directory"""
42
- try:
43
- with zipfile.ZipFile(zip_path, 'r') as zip_ref:
44
- zip_ref.extractall(extract_to)
45
- return True
46
- except Exception as e:
47
- logger.error(f"Error extracting ZIP: {str(e)}")
48
- return False
49
-
50
- def extract_tar(tar_path, extract_to):
51
- """Extract TAR file to specified directory"""
52
- try:
53
- import tarfile
54
- with tarfile.open(tar_path, 'r:*') as tar_ref:
55
- tar_ref.extractall(extract_to)
56
- return True
57
- except Exception as e:
58
- logger.error(f"Error extracting TAR: {str(e)}")
59
- return False
60
-
61
- def create_zip_from_files(files, zip_path):
62
- """Create a ZIP file from uploaded files"""
63
- with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
64
- for file in files:
65
- if file.filename:
66
- # Save file temporarily
67
- temp_path = os.path.join(tempfile.gettempdir(), secure_filename(file.filename))
68
- file.save(temp_path)
69
-
70
- # Add to ZIP with the original filename
71
- zipf.write(temp_path, file.filename)
72
-
73
- # Clean up temp file
74
- os.remove(temp_path)
75
-
76
- def upload_to_huggingface(upload_id, folder_path, repo_id, hf_token, commit_message, repo_type="space", create_repo=False):
77
- """Upload folder to Hugging Face in a separate thread"""
78
- try:
79
- logger.info(f"Starting upload {upload_id} to {repo_id}")
80
- upload_status[upload_id] = "Initializing..."
81
- upload_progress[upload_id] = 0
82
-
83
- # Initialize API
84
- logger.info(f"Initializing HfApi for {upload_id}")
85
- api = HfApi(token=hf_token)
86
-
87
- # Test token validity
88
- try:
89
- logger.info(f"Testing token validity for {upload_id}")
90
- user_info = api.whoami()
91
- logger.info(f"Token valid for user: {user_info.get('name', 'Unknown')}")
92
- except Exception as e:
93
- logger.error(f"Token validation failed for {upload_id}: {str(e)}")
94
- upload_status[upload_id] = f"Invalid token: {str(e)}"
95
- return
96
-
97
- # Create repository if requested
98
- if create_repo:
99
- try:
100
- upload_status[upload_id] = "Creating repository..."
101
- upload_progress[upload_id] = 10
102
- api.create_repo(repo_id=repo_id, repo_type=repo_type, exist_ok=True)
103
- logger.info(f"Repository created/exists: {repo_id}")
104
- except Exception as e:
105
- logger.error(f"Repository creation failed for {upload_id}: {str(e)}")
106
- upload_status[upload_id] = f"Repository creation failed: {str(e)}"
107
- return
108
-
109
- upload_status[upload_id] = "Uploading to Hugging Face..."
110
- upload_progress[upload_id] = 25
111
-
112
- logger.info(f"Starting upload for {upload_id}: {folder_path} -> {repo_id}")
113
-
114
- # Always upload as folder to maintain directory structure
115
- if os.path.isfile(folder_path):
116
- logger.info(f"Converting single file to folder structure: {folder_path}")
117
- # Create a temporary folder for single file
118
- temp_folder = os.path.join(os.path.dirname(folder_path), f"temp_folder_{upload_id}")
119
- os.makedirs(temp_folder, exist_ok=True)
120
-
121
- # Copy the file to the temporary folder
122
- filename = os.path.basename(folder_path)
123
- temp_file_path = os.path.join(temp_folder, filename)
124
- shutil.copy2(folder_path, temp_file_path)
125
-
126
- # Upload the temporary folder
127
- upload_folder(
128
- folder_path=temp_folder,
129
- repo_id=repo_id,
130
- repo_type=repo_type,
131
- token=hf_token,
132
- commit_message=commit_message,
133
- ignore_patterns=[".git", "__pycache__", "*.pyc", ".env", "*.log", ".DS_Store", "Thumbs.db"]
134
- )
135
-
136
- # Clean up temporary folder
137
- shutil.rmtree(temp_folder)
138
-
139
- else:
140
- logger.info(f"Uploading folder: {folder_path}")
141
- # Folder upload
142
- upload_folder(
143
- folder_path=folder_path,
144
- repo_id=repo_id,
145
- repo_type=repo_type,
146
- token=hf_token,
147
- commit_message=commit_message,
148
- ignore_patterns=[".git", "__pycache__", "*.pyc", ".env", "*.log", ".DS_Store", "Thumbs.db"]
149
- )
150
-
151
- upload_progress[upload_id] = 100
152
- upload_status[upload_id] = "Upload completed successfully!"
153
- logger.info(f"Upload {upload_id} completed successfully")
154
-
155
- # Add to history
156
- upload_history.append({
157
- 'upload_id': upload_id,
158
- 'repo_id': repo_id,
159
- 'repo_type': repo_type,
160
- 'status': 'success',
161
- 'timestamp': datetime.now().isoformat(),
162
- 'message': 'Upload completed successfully!'
163
- })
164
-
165
- except Exception as e:
166
- error_msg = f"Upload error: {str(e)}"
167
- logger.error(f"Upload {upload_id} failed: {error_msg}")
168
- logger.error(f"Full traceback: {traceback.format_exc()}")
169
- upload_status[upload_id] = error_msg
170
- upload_progress[upload_id] = 0
171
-
172
- # Add to history
173
- upload_history.append({
174
- 'upload_id': upload_id,
175
- 'repo_id': repo_id,
176
- 'repo_type': repo_type,
177
- 'status': 'error',
178
- 'timestamp': datetime.now().isoformat(),
179
- 'message': error_msg
180
- })
181
-
182
- @app.route('/')
183
- def index():
184
- return render_template('index.html')
185
-
186
- @app.route('/upload', methods=['POST'])
187
- def upload_file():
188
- try:
189
- # Get form data
190
- hf_token = request.form.get('hf_token')
191
- repo_id = request.form.get('repo_id')
192
- repo_type = request.form.get('repo_type', 'space')
193
- commit_message = request.form.get('commit_message', 'Upload via HuggingFace Uploader Pro')
194
- create_repo = request.form.get('create_repo') == 'true'
195
-
196
- logger.info(f"Upload request: repo_id={repo_id}, repo_type={repo_type}, create_repo={create_repo}")
197
-
198
- if not hf_token or not repo_id:
199
- return jsonify({'error': 'Hugging Face token and repository ID are required'}), 400
200
-
201
- # Check if files were uploaded
202
- if 'files' not in request.files:
203
- return jsonify({'error': 'No files selected'}), 400
204
-
205
- files = request.files.getlist('files')
206
- if not files or all(file.filename == '' for file in files):
207
- return jsonify({'error': 'No files selected'}), 400
208
-
209
- # Generate unique upload ID and timestamp
210
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
211
- upload_id = f"upload_{timestamp}"
212
-
213
- # Handle multiple files and folder structures
214
- if len(files) == 1 and not any('/' in file.filename for file in files):
215
- # Single file upload
216
- file = files[0]
217
- if file and allowed_file(file.filename):
218
- filename = secure_filename(file.filename)
219
- unique_filename = f"{timestamp}_{filename}"
220
-
221
- # Save uploaded file
222
- file_path = os.path.join(app.config['UPLOAD_FOLDER'], unique_filename)
223
- file.save(file_path)
224
- logger.info(f"File saved: {file_path}")
225
-
226
- # Handle archive files
227
- if filename.lower().endswith('.zip'):
228
- extract_folder = os.path.join(app.config['UPLOAD_FOLDER'], f"extracted_{timestamp}")
229
- os.makedirs(extract_folder, exist_ok=True)
230
- if extract_zip(file_path, extract_folder):
231
- upload_path = extract_folder
232
- logger.info(f"ZIP extracted to: {extract_folder}")
233
- else:
234
- return jsonify({'error': 'Failed to extract ZIP file'}), 400
235
- elif filename.lower().endswith(('.tar', '.tar.gz', '.tgz')):
236
- extract_folder = os.path.join(app.config['UPLOAD_FOLDER'], f"extracted_{timestamp}")
237
- os.makedirs(extract_folder, exist_ok=True)
238
- if extract_tar(file_path, extract_folder):
239
- upload_path = extract_folder
240
- logger.info(f"TAR extracted to: {extract_folder}")
241
- else:
242
- return jsonify({'error': 'Failed to extract TAR file'}), 400
243
- else:
244
- upload_path = file_path
245
- else:
246
- return jsonify({'error': 'File type not allowed'}), 400
247
- else:
248
- # Multiple files or folder structure - create folder structure
249
- folder_path = os.path.join(app.config['UPLOAD_FOLDER'], f"folder_{timestamp}")
250
- os.makedirs(folder_path, exist_ok=True)
251
-
252
- # Save all files to the folder, preserving directory structure
253
- for file in files:
254
- if file and file.filename and allowed_file(file.filename):
255
- # Use the original filename which may include directory structure
256
- filename = file.filename
257
- # Ensure the filename is safe but preserve directory structure
258
- safe_filename = filename.replace('\\', '/').strip('/')
259
- file_path = os.path.join(folder_path, safe_filename)
260
-
261
- # Create subdirectories if needed (for folder uploads)
262
- os.makedirs(os.path.dirname(file_path), exist_ok=True)
263
- file.save(file_path)
264
- logger.info(f"File saved: {file_path}")
265
- elif file and file.filename:
266
- return jsonify({'error': f'File type not allowed: {file.filename}'}), 400
267
-
268
- upload_path = folder_path
269
- logger.info(f"Folder created: {folder_path}")
270
-
271
- # Start upload in background thread
272
- thread = threading.Thread(
273
- target=upload_to_huggingface,
274
- args=(upload_id, upload_path, repo_id, hf_token, commit_message, repo_type, create_repo)
275
- )
276
- thread.daemon = True
277
- thread.start()
278
-
279
- return jsonify({
280
- 'success': True,
281
- 'upload_id': upload_id,
282
- 'message': 'Upload started successfully!'
283
- })
284
-
285
- except Exception as e:
286
- logger.error(f"Upload endpoint error: {str(e)}")
287
- logger.error(f"Full traceback: {traceback.format_exc()}")
288
- return jsonify({'error': str(e)}), 500
289
-
290
- @app.route('/progress/<upload_id>')
291
- def get_progress(upload_id):
292
- """Get upload progress"""
293
- progress = upload_progress.get(upload_id, 0)
294
- status = upload_status.get(upload_id, "Unknown")
295
-
296
- return jsonify({
297
- 'progress': progress,
298
- 'status': status,
299
- 'completed': progress == 100 and 'error' not in status.lower()
300
- })
301
-
302
- @app.route('/history')
303
- def get_history():
304
- """Get upload history"""
305
- return jsonify({'history': upload_history})
306
-
307
- @app.route('/cleanup')
308
- def cleanup():
309
- """Clean up old uploaded files"""
310
- try:
311
- # Remove files older than 1 hour
312
- cutoff_time = time.time() - 3600 # 1 hour
313
-
314
- for filename in os.listdir(app.config['UPLOAD_FOLDER']):
315
- file_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
316
- if os.path.getctime(file_path) < cutoff_time:
317
- if os.path.isfile(file_path):
318
- os.remove(file_path)
319
- elif os.path.isdir(file_path):
320
- shutil.rmtree(file_path)
321
-
322
- return jsonify({'message': 'Cleanup completed'})
323
- except Exception as e:
324
- return jsonify({'error': str(e)}), 500
325
-
326
- if __name__ == '__main__':
327
- # For local development
328
- app.run(debug=True, host='0.0.0.0', port=7860)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
huggingface-uploader/requirements.txt DELETED
@@ -1,3 +0,0 @@
1
- Flask
2
- huggingface-hub
3
- Werkzeug
 
 
 
 
huggingface-uploader/templates/index.html DELETED
@@ -1,481 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>HuggingFace Uploader</title>
7
- <style>
8
- body {
9
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
10
- max-width: 800px;
11
- margin: 0 auto;
12
- padding: 20px;
13
- background-color: #f8f9fa;
14
- }
15
- .container {
16
- background: white;
17
- padding: 30px;
18
- border-radius: 10px;
19
- box-shadow: 0 2px 10px rgba(0,0,0,0.1);
20
- }
21
- h1 {
22
- color: #333;
23
- text-align: center;
24
- margin-bottom: 30px;
25
- }
26
- .form-group {
27
- margin-bottom: 20px;
28
- }
29
- label {
30
- display: block;
31
- margin-bottom: 5px;
32
- font-weight: 600;
33
- color: #555;
34
- }
35
- input, select, textarea {
36
- width: 100%;
37
- padding: 12px;
38
- border: 2px solid #ddd;
39
- border-radius: 5px;
40
- font-size: 14px;
41
- transition: border-color 0.3s;
42
- }
43
- input:focus, select:focus, textarea:focus {
44
- outline: none;
45
- border-color: #007bff;
46
- }
47
- .file-input-container {
48
- border: 2px dashed #ddd;
49
- border-radius: 5px;
50
- padding: 20px;
51
- text-align: center;
52
- background-color: #f9f9f9;
53
- transition: all 0.3s;
54
- }
55
- .file-input-container:hover {
56
- border-color: #007bff;
57
- background-color: #f0f8ff;
58
- }
59
- .file-input-container.drag-over {
60
- border-color: #007bff;
61
- background-color: #e6f3ff;
62
- }
63
- #files {
64
- display: none;
65
- }
66
- .file-input-label {
67
- display: inline-block;
68
- background-color: #007bff;
69
- color: white;
70
- padding: 10px 20px;
71
- border-radius: 5px;
72
- cursor: pointer;
73
- margin-bottom: 10px;
74
- transition: background-color 0.3s;
75
- }
76
- .file-input-label:hover {
77
- background-color: #0056b3;
78
- }
79
- .upload-modes {
80
- display: flex;
81
- gap: 10px;
82
- margin-bottom: 15px;
83
- }
84
- .upload-mode {
85
- flex: 1;
86
- padding: 10px;
87
- border: 2px solid #ddd;
88
- border-radius: 5px;
89
- text-align: center;
90
- cursor: pointer;
91
- transition: all 0.3s;
92
- }
93
- .upload-mode.active {
94
- border-color: #007bff;
95
- background-color: #e6f3ff;
96
- }
97
- .upload-mode:hover {
98
- border-color: #007bff;
99
- }
100
- .file-list {
101
- margin-top: 15px;
102
- padding: 10px;
103
- background-color: #f8f9fa;
104
- border-radius: 5px;
105
- max-height: 200px;
106
- overflow-y: auto;
107
- }
108
- .file-item {
109
- display: flex;
110
- justify-content: space-between;
111
- align-items: center;
112
- padding: 5px 0;
113
- border-bottom: 1px solid #eee;
114
- }
115
- .file-item:last-child {
116
- border-bottom: none;
117
- }
118
- .remove-file {
119
- color: #dc3545;
120
- cursor: pointer;
121
- font-weight: bold;
122
- }
123
- .remove-file:hover {
124
- text-decoration: underline;
125
- }
126
- .btn {
127
- background-color: #28a745;
128
- color: white;
129
- padding: 12px 30px;
130
- border: none;
131
- border-radius: 5px;
132
- font-size: 16px;
133
- cursor: pointer;
134
- transition: background-color 0.3s;
135
- width: 100%;
136
- }
137
- .btn:hover {
138
- background-color: #218838;
139
- }
140
- .btn:disabled {
141
- background-color: #6c757d;
142
- cursor: not-allowed;
143
- }
144
- .progress-container {
145
- margin-top: 20px;
146
- display: none;
147
- }
148
- .progress-bar {
149
- width: 100%;
150
- height: 20px;
151
- background-color: #e9ecef;
152
- border-radius: 10px;
153
- overflow: hidden;
154
- }
155
- .progress-fill {
156
- height: 100%;
157
- background-color: #28a745;
158
- transition: width 0.3s ease;
159
- width: 0%;
160
- }
161
- .progress-text {
162
- text-align: center;
163
- margin-top: 10px;
164
- font-weight: 600;
165
- }
166
- .error {
167
- color: #dc3545;
168
- background-color: #f8d7da;
169
- border: 1px solid #f5c6cb;
170
- padding: 10px;
171
- border-radius: 5px;
172
- margin-top: 10px;
173
- }
174
- .success {
175
- color: #155724;
176
- background-color: #d4edda;
177
- border: 1px solid #c3e6cb;
178
- padding: 10px;
179
- border-radius: 5px;
180
- margin-top: 10px;
181
- }
182
- .help-text {
183
- font-size: 12px;
184
- color: #6c757d;
185
- margin-top: 5px;
186
- }
187
- .folder-instructions {
188
- background-color: #fff3cd;
189
- border: 1px solid #ffeaa7;
190
- padding: 10px;
191
- border-radius: 5px;
192
- margin-top: 10px;
193
- font-size: 14px;
194
- }
195
- </style>
196
- </head>
197
- <body>
198
- <div class="container">
199
- <h1>🤗 HuggingFace Uploader</h1>
200
-
201
- <form id="uploadForm" enctype="multipart/form-data">
202
- <div class="form-group">
203
- <label for="hf_token">HuggingFace Token:</label>
204
- <input type="password" id="hf_token" name="hf_token" required
205
- placeholder="hf_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx">
206
- <div class="help-text">Get your token from: https://huggingface.co/settings/tokens</div>
207
- </div>
208
-
209
- <div class="form-group">
210
- <label for="repo_id">Repository ID:</label>
211
- <input type="text" id="repo_id" name="repo_id" required
212
- placeholder="your-username/repository-name">
213
- <div class="help-text">Format: username/repository-name</div>
214
- </div>
215
-
216
- <div class="form-group">
217
- <label for="repo_type">Repository Type:</label>
218
- <select id="repo_type" name="repo_type">
219
- <option value="space">Space</option>
220
- <option value="model">Model</option>
221
- <option value="dataset">Dataset</option>
222
- </select>
223
- </div>
224
-
225
- <div class="form-group">
226
- <label for="commit_message">Commit Message:</label>
227
- <textarea id="commit_message" name="commit_message" rows="2"
228
- placeholder="Upload via Flask App">Upload via Flask App</textarea>
229
- </div>
230
-
231
- <div class="form-group">
232
- <label>Upload Type:</label>
233
- <div class="upload-modes">
234
- <div class="upload-mode active" data-mode="folder">
235
- 📁 Folder
236
- </div>
237
- <div class="upload-mode" data-mode="files">
238
- 📄 Files
239
- </div>
240
- <div class="upload-mode" data-mode="zip">
241
- 📦 ZIP Archive
242
- </div>
243
- </div>
244
- </div>
245
-
246
- <div class="form-group">
247
- <label>Select Files:</label>
248
- <div class="file-input-container" id="fileInputContainer">
249
- <label for="files" class="file-input-label">
250
- 📎 Choose Files or Drag & Drop
251
- </label>
252
- <input type="file" id="files" name="files" multiple webkitdirectory style="display: none;">
253
- <div id="uploadInstructions">
254
- <p>📁 <strong>Folder Mode:</strong> Click "Choose Files" and select a folder to upload all its contents</p>
255
- <p>All files and subdirectories will be uploaded maintaining the folder structure</p>
256
- </div>
257
- </div>
258
- <div class="folder-instructions">
259
- <strong>Note:</strong> When uploading folders, the browser will ask you to select a folder.
260
- All files in the selected folder (including subfolders) will be uploaded to your HuggingFace repository with the same directory structure.
261
- </div>
262
- <div id="fileList" class="file-list" style="display: none;"></div>
263
- </div>
264
-
265
- <button type="submit" class="btn" id="uploadBtn">🚀 Upload to HuggingFace</button>
266
- </form>
267
-
268
- <div class="progress-container" id="progressContainer">
269
- <div class="progress-bar">
270
- <div class="progress-fill" id="progressFill"></div>
271
- </div>
272
- <div class="progress-text" id="progressText">Starting upload...</div>
273
- </div>
274
-
275
- <div id="message"></div>
276
- </div>
277
-
278
- <script>
279
- let selectedFiles = [];
280
- let currentMode = 'folder';
281
-
282
- // Upload mode switching
283
- document.querySelectorAll('.upload-mode').forEach(mode => {
284
- mode.addEventListener('click', function() {
285
- document.querySelectorAll('.upload-mode').forEach(m => m.classList.remove('active'));
286
- this.classList.add('active');
287
- currentMode = this.dataset.mode;
288
-
289
- const fileInput = document.getElementById('files');
290
- const instructions = document.getElementById('uploadInstructions');
291
-
292
- if (currentMode === 'zip') {
293
- fileInput.removeAttribute('webkitdirectory');
294
- fileInput.removeAttribute('multiple');
295
- fileInput.setAttribute('accept', '.zip');
296
- instructions.innerHTML = '<p>📦 Select a ZIP file to upload</p>';
297
- } else if (currentMode === 'folder') {
298
- fileInput.setAttribute('webkitdirectory', '');
299
- fileInput.setAttribute('multiple', '');
300
- fileInput.removeAttribute('accept');
301
- instructions.innerHTML = `
302
- <p>📁 <strong>Folder Mode:</strong> Click "Choose Files" and select a folder to upload all its contents</p>
303
- <p>All files and subdirectories will be uploaded maintaining the folder structure</p>
304
- `;
305
- } else {
306
- fileInput.removeAttribute('webkitdirectory');
307
- fileInput.setAttribute('multiple', '');
308
- fileInput.removeAttribute('accept');
309
- instructions.innerHTML = `
310
- <p>📄 <strong>Files Mode:</strong> Select one or more individual files</p>
311
- <p>Files will be uploaded to the root of your repository</p>
312
- `;
313
- }
314
-
315
- // Clear previous selections
316
- selectedFiles = [];
317
- updateFileList();
318
- });
319
- });
320
-
321
- // File input handling
322
- const fileInput = document.getElementById('files');
323
- const fileInputContainer = document.getElementById('fileInputContainer');
324
- const fileList = document.getElementById('fileList');
325
-
326
- fileInput.addEventListener('change', function(e) {
327
- selectedFiles = Array.from(e.target.files);
328
- updateFileList();
329
- });
330
-
331
- // Drag and drop handling
332
- fileInputContainer.addEventListener('dragover', function(e) {
333
- e.preventDefault();
334
- fileInputContainer.classList.add('drag-over');
335
- });
336
-
337
- fileInputContainer.addEventListener('dragleave', function(e) {
338
- e.preventDefault();
339
- fileInputContainer.classList.remove('drag-over');
340
- });
341
-
342
- fileInputContainer.addEventListener('drop', function(e) {
343
- e.preventDefault();
344
- fileInputContainer.classList.remove('drag-over');
345
-
346
- const items = Array.from(e.dataTransfer.items);
347
- selectedFiles = [];
348
-
349
- items.forEach(item => {
350
- if (item.kind === 'file') {
351
- selectedFiles.push(item.getAsFile());
352
- }
353
- });
354
-
355
- updateFileList();
356
- });
357
-
358
- function updateFileList() {
359
- if (selectedFiles.length === 0) {
360
- fileList.style.display = 'none';
361
- return;
362
- }
363
-
364
- fileList.style.display = 'block';
365
- fileList.innerHTML = '';
366
-
367
- selectedFiles.forEach((file, index) => {
368
- const fileItem = document.createElement('div');
369
- fileItem.className = 'file-item';
370
- const displayName = file.webkitRelativePath || file.name;
371
- fileItem.innerHTML = `
372
- <span>📄 ${displayName} (${formatFileSize(file.size)})</span>
373
- <span class="remove-file" onclick="removeFile(${index})">✕</span>
374
- `;
375
- fileList.appendChild(fileItem);
376
- });
377
- }
378
-
379
- function removeFile(index) {
380
- selectedFiles.splice(index, 1);
381
- updateFileList();
382
- }
383
-
384
- function formatFileSize(bytes) {
385
- if (bytes === 0) return '0 Bytes';
386
- const k = 1024;
387
- const sizes = ['Bytes', 'KB', 'MB', 'GB'];
388
- const i = Math.floor(Math.log(bytes) / Math.log(k));
389
- return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
390
- }
391
-
392
- // Form submission
393
- document.getElementById('uploadForm').addEventListener('submit', function(e) {
394
- e.preventDefault();
395
-
396
- if (selectedFiles.length === 0) {
397
- showMessage('Please select files to upload', 'error');
398
- return;
399
- }
400
-
401
- const formData = new FormData();
402
- formData.append('hf_token', document.getElementById('hf_token').value);
403
- formData.append('repo_id', document.getElementById('repo_id').value);
404
- formData.append('repo_type', document.getElementById('repo_type').value);
405
- formData.append('commit_message', document.getElementById('commit_message').value);
406
-
407
- // Add files to form data
408
- selectedFiles.forEach(file => {
409
- formData.append('files', file);
410
- });
411
-
412
- // Show progress
413
- document.getElementById('progressContainer').style.display = 'block';
414
- document.getElementById('uploadBtn').disabled = true;
415
- document.getElementById('uploadBtn').textContent = 'Uploading...';
416
-
417
- fetch('/upload', {
418
- method: 'POST',
419
- body: formData
420
- })
421
- .then(response => response.json())
422
- .then(data => {
423
- if (data.success) {
424
- showMessage(data.message, 'success');
425
- checkProgress(data.upload_id);
426
- } else {
427
- showMessage(data.error || 'Upload failed', 'error');
428
- resetForm();
429
- }
430
- })
431
- .catch(error => {
432
- showMessage('Error: ' + error.message, 'error');
433
- resetForm();
434
- });
435
- });
436
-
437
- function checkProgress(uploadId) {
438
- const progressFill = document.getElementById('progressFill');
439
- const progressText = document.getElementById('progressText');
440
-
441
- const interval = setInterval(() => {
442
- fetch(`/progress/${uploadId}`)
443
- .then(response => response.json())
444
- .then(data => {
445
- progressFill.style.width = data.progress + '%';
446
- progressText.textContent = data.status;
447
-
448
- if (data.completed || data.progress === 100) {
449
- clearInterval(interval);
450
- if (data.status.includes('Error')) {
451
- showMessage(data.status, 'error');
452
- } else {
453
- showMessage('Upload completed successfully! 🎉', 'success');
454
- }
455
- resetForm();
456
- }
457
- })
458
- .catch(error => {
459
- clearInterval(interval);
460
- showMessage('Error checking progress: ' + error.message, 'error');
461
- resetForm();
462
- });
463
- }, 2000);
464
- }
465
-
466
- function showMessage(message, type) {
467
- const messageDiv = document.getElementById('message');
468
- messageDiv.className = type;
469
- messageDiv.textContent = message;
470
- messageDiv.style.display = 'block';
471
- }
472
-
473
- function resetForm() {
474
- document.getElementById('uploadBtn').disabled = false;
475
- document.getElementById('uploadBtn').textContent = '🚀 Upload to HuggingFace';
476
- document.getElementById('progressContainer').style.display = 'none';
477
- document.getElementById('progressFill').style.width = '0%';
478
- }
479
- </script>
480
- </body>
481
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
huggingface-uploader/templates/index1.html DELETED
@@ -1,481 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>HuggingFace Uploader</title>
7
- <style>
8
- body {
9
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
10
- max-width: 800px;
11
- margin: 0 auto;
12
- padding: 20px;
13
- background-color: #f8f9fa;
14
- }
15
- .container {
16
- background: white;
17
- padding: 30px;
18
- border-radius: 10px;
19
- box-shadow: 0 2px 10px rgba(0,0,0,0.1);
20
- }
21
- h1 {
22
- color: #333;
23
- text-align: center;
24
- margin-bottom: 30px;
25
- }
26
- .form-group {
27
- margin-bottom: 20px;
28
- }
29
- label {
30
- display: block;
31
- margin-bottom: 5px;
32
- font-weight: 600;
33
- color: #555;
34
- }
35
- input, select, textarea {
36
- width: 100%;
37
- padding: 12px;
38
- border: 2px solid #ddd;
39
- border-radius: 5px;
40
- font-size: 14px;
41
- transition: border-color 0.3s;
42
- }
43
- input:focus, select:focus, textarea:focus {
44
- outline: none;
45
- border-color: #007bff;
46
- }
47
- .file-input-container {
48
- border: 2px dashed #ddd;
49
- border-radius: 5px;
50
- padding: 20px;
51
- text-align: center;
52
- background-color: #f9f9f9;
53
- transition: all 0.3s;
54
- }
55
- .file-input-container:hover {
56
- border-color: #007bff;
57
- background-color: #f0f8ff;
58
- }
59
- .file-input-container.drag-over {
60
- border-color: #007bff;
61
- background-color: #e6f3ff;
62
- }
63
- #files {
64
- display: none;
65
- }
66
- .file-input-label {
67
- display: inline-block;
68
- background-color: #007bff;
69
- color: white;
70
- padding: 10px 20px;
71
- border-radius: 5px;
72
- cursor: pointer;
73
- margin-bottom: 10px;
74
- transition: background-color 0.3s;
75
- }
76
- .file-input-label:hover {
77
- background-color: #0056b3;
78
- }
79
- .upload-modes {
80
- display: flex;
81
- gap: 10px;
82
- margin-bottom: 15px;
83
- }
84
- .upload-mode {
85
- flex: 1;
86
- padding: 10px;
87
- border: 2px solid #ddd;
88
- border-radius: 5px;
89
- text-align: center;
90
- cursor: pointer;
91
- transition: all 0.3s;
92
- }
93
- .upload-mode.active {
94
- border-color: #007bff;
95
- background-color: #e6f3ff;
96
- }
97
- .upload-mode:hover {
98
- border-color: #007bff;
99
- }
100
- .file-list {
101
- margin-top: 15px;
102
- padding: 10px;
103
- background-color: #f8f9fa;
104
- border-radius: 5px;
105
- max-height: 200px;
106
- overflow-y: auto;
107
- }
108
- .file-item {
109
- display: flex;
110
- justify-content: space-between;
111
- align-items: center;
112
- padding: 5px 0;
113
- border-bottom: 1px solid #eee;
114
- }
115
- .file-item:last-child {
116
- border-bottom: none;
117
- }
118
- .remove-file {
119
- color: #dc3545;
120
- cursor: pointer;
121
- font-weight: bold;
122
- }
123
- .remove-file:hover {
124
- text-decoration: underline;
125
- }
126
- .btn {
127
- background-color: #28a745;
128
- color: white;
129
- padding: 12px 30px;
130
- border: none;
131
- border-radius: 5px;
132
- font-size: 16px;
133
- cursor: pointer;
134
- transition: background-color 0.3s;
135
- width: 100%;
136
- }
137
- .btn:hover {
138
- background-color: #218838;
139
- }
140
- .btn:disabled {
141
- background-color: #6c757d;
142
- cursor: not-allowed;
143
- }
144
- .progress-container {
145
- margin-top: 20px;
146
- display: none;
147
- }
148
- .progress-bar {
149
- width: 100%;
150
- height: 20px;
151
- background-color: #e9ecef;
152
- border-radius: 10px;
153
- overflow: hidden;
154
- }
155
- .progress-fill {
156
- height: 100%;
157
- background-color: #28a745;
158
- transition: width 0.3s ease;
159
- width: 0%;
160
- }
161
- .progress-text {
162
- text-align: center;
163
- margin-top: 10px;
164
- font-weight: 600;
165
- }
166
- .error {
167
- color: #dc3545;
168
- background-color: #f8d7da;
169
- border: 1px solid #f5c6cb;
170
- padding: 10px;
171
- border-radius: 5px;
172
- margin-top: 10px;
173
- }
174
- .success {
175
- color: #155724;
176
- background-color: #d4edda;
177
- border: 1px solid #c3e6cb;
178
- padding: 10px;
179
- border-radius: 5px;
180
- margin-top: 10px;
181
- }
182
- .help-text {
183
- font-size: 12px;
184
- color: #6c757d;
185
- margin-top: 5px;
186
- }
187
- .folder-instructions {
188
- background-color: #fff3cd;
189
- border: 1px solid #ffeaa7;
190
- padding: 10px;
191
- border-radius: 5px;
192
- margin-top: 10px;
193
- font-size: 14px;
194
- }
195
- </style>
196
- </head>
197
- <body>
198
- <div class="container">
199
- <h1>🤗 HuggingFace Uploader</h1>
200
-
201
- <form id="uploadForm" enctype="multipart/form-data">
202
- <div class="form-group">
203
- <label for="hf_token">HuggingFace Token:</label>
204
- <input type="password" id="hf_token" name="hf_token" required
205
- placeholder="hf_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx">
206
- <div class="help-text">Get your token from: https://huggingface.co/settings/tokens</div>
207
- </div>
208
-
209
- <div class="form-group">
210
- <label for="repo_id">Repository ID:</label>
211
- <input type="text" id="repo_id" name="repo_id" required
212
- placeholder="your-username/repository-name">
213
- <div class="help-text">Format: username/repository-name</div>
214
- </div>
215
-
216
- <div class="form-group">
217
- <label for="repo_type">Repository Type:</label>
218
- <select id="repo_type" name="repo_type">
219
- <option value="space">Space</option>
220
- <option value="model">Model</option>
221
- <option value="dataset">Dataset</option>
222
- </select>
223
- </div>
224
-
225
- <div class="form-group">
226
- <label for="commit_message">Commit Message:</label>
227
- <textarea id="commit_message" name="commit_message" rows="2"
228
- placeholder="Upload via Flask App">Upload via Flask App</textarea>
229
- </div>
230
-
231
- <div class="form-group">
232
- <label>Upload Type:</label>
233
- <div class="upload-modes">
234
- <div class="upload-mode active" data-mode="folder">
235
- 📁 Folder
236
- </div>
237
- <div class="upload-mode" data-mode="files">
238
- 📄 Files
239
- </div>
240
- <div class="upload-mode" data-mode="zip">
241
- 📦 ZIP Archive
242
- </div>
243
- </div>
244
- </div>
245
-
246
- <div class="form-group">
247
- <label>Select Files:</label>
248
- <div class="file-input-container" id="fileInputContainer">
249
- <label for="files" class="file-input-label">
250
- 📎 Choose Files or Drag & Drop
251
- </label>
252
- <input type="file" id="files" name="files" multiple webkitdirectory style="display: none;">
253
- <div id="uploadInstructions">
254
- <p>📁 <strong>Folder Mode:</strong> Click "Choose Files" and select a folder to upload all its contents</p>
255
- <p>All files and subdirectories will be uploaded maintaining the folder structure</p>
256
- </div>
257
- </div>
258
- <div class="folder-instructions">
259
- <strong>Note:</strong> When uploading folders, the browser will ask you to select a folder.
260
- All files in the selected folder (including subfolders) will be uploaded to your HuggingFace repository with the same directory structure.
261
- </div>
262
- <div id="fileList" class="file-list" style="display: none;"></div>
263
- </div>
264
-
265
- <button type="submit" class="btn" id="uploadBtn">🚀 Upload to HuggingFace</button>
266
- </form>
267
-
268
- <div class="progress-container" id="progressContainer">
269
- <div class="progress-bar">
270
- <div class="progress-fill" id="progressFill"></div>
271
- </div>
272
- <div class="progress-text" id="progressText">Starting upload...</div>
273
- </div>
274
-
275
- <div id="message"></div>
276
- </div>
277
-
278
- <script>
279
- let selectedFiles = [];
280
- let currentMode = 'folder';
281
-
282
- // Upload mode switching
283
- document.querySelectorAll('.upload-mode').forEach(mode => {
284
- mode.addEventListener('click', function() {
285
- document.querySelectorAll('.upload-mode').forEach(m => m.classList.remove('active'));
286
- this.classList.add('active');
287
- currentMode = this.dataset.mode;
288
-
289
- const fileInput = document.getElementById('files');
290
- const instructions = document.getElementById('uploadInstructions');
291
-
292
- if (currentMode === 'zip') {
293
- fileInput.removeAttribute('webkitdirectory');
294
- fileInput.removeAttribute('multiple');
295
- fileInput.setAttribute('accept', '.zip');
296
- instructions.innerHTML = '<p>📦 Select a ZIP file to upload</p>';
297
- } else if (currentMode === 'folder') {
298
- fileInput.setAttribute('webkitdirectory', '');
299
- fileInput.setAttribute('multiple', '');
300
- fileInput.removeAttribute('accept');
301
- instructions.innerHTML = `
302
- <p>📁 <strong>Folder Mode:</strong> Click "Choose Files" and select a folder to upload all its contents</p>
303
- <p>All files and subdirectories will be uploaded maintaining the folder structure</p>
304
- `;
305
- } else {
306
- fileInput.removeAttribute('webkitdirectory');
307
- fileInput.setAttribute('multiple', '');
308
- fileInput.removeAttribute('accept');
309
- instructions.innerHTML = `
310
- <p>📄 <strong>Files Mode:</strong> Select one or more individual files</p>
311
- <p>Files will be uploaded to the root of your repository</p>
312
- `;
313
- }
314
-
315
- // Clear previous selections
316
- selectedFiles = [];
317
- updateFileList();
318
- });
319
- });
320
-
321
- // File input handling
322
- const fileInput = document.getElementById('files');
323
- const fileInputContainer = document.getElementById('fileInputContainer');
324
- const fileList = document.getElementById('fileList');
325
-
326
- fileInput.addEventListener('change', function(e) {
327
- selectedFiles = Array.from(e.target.files);
328
- updateFileList();
329
- });
330
-
331
- // Drag and drop handling
332
- fileInputContainer.addEventListener('dragover', function(e) {
333
- e.preventDefault();
334
- fileInputContainer.classList.add('drag-over');
335
- });
336
-
337
- fileInputContainer.addEventListener('dragleave', function(e) {
338
- e.preventDefault();
339
- fileInputContainer.classList.remove('drag-over');
340
- });
341
-
342
- fileInputContainer.addEventListener('drop', function(e) {
343
- e.preventDefault();
344
- fileInputContainer.classList.remove('drag-over');
345
-
346
- const items = Array.from(e.dataTransfer.items);
347
- selectedFiles = [];
348
-
349
- items.forEach(item => {
350
- if (item.kind === 'file') {
351
- selectedFiles.push(item.getAsFile());
352
- }
353
- });
354
-
355
- updateFileList();
356
- });
357
-
358
- function updateFileList() {
359
- if (selectedFiles.length === 0) {
360
- fileList.style.display = 'none';
361
- return;
362
- }
363
-
364
- fileList.style.display = 'block';
365
- fileList.innerHTML = '';
366
-
367
- selectedFiles.forEach((file, index) => {
368
- const fileItem = document.createElement('div');
369
- fileItem.className = 'file-item';
370
- const displayName = file.webkitRelativePath || file.name;
371
- fileItem.innerHTML = `
372
- <span>📄 ${displayName} (${formatFileSize(file.size)})</span>
373
- <span class="remove-file" onclick="removeFile(${index})">✕</span>
374
- `;
375
- fileList.appendChild(fileItem);
376
- });
377
- }
378
-
379
- function removeFile(index) {
380
- selectedFiles.splice(index, 1);
381
- updateFileList();
382
- }
383
-
384
- function formatFileSize(bytes) {
385
- if (bytes === 0) return '0 Bytes';
386
- const k = 1024;
387
- const sizes = ['Bytes', 'KB', 'MB', 'GB'];
388
- const i = Math.floor(Math.log(bytes) / Math.log(k));
389
- return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
390
- }
391
-
392
- // Form submission
393
- document.getElementById('uploadForm').addEventListener('submit', function(e) {
394
- e.preventDefault();
395
-
396
- if (selectedFiles.length === 0) {
397
- showMessage('Please select files to upload', 'error');
398
- return;
399
- }
400
-
401
- const formData = new FormData();
402
- formData.append('hf_token', document.getElementById('hf_token').value);
403
- formData.append('repo_id', document.getElementById('repo_id').value);
404
- formData.append('repo_type', document.getElementById('repo_type').value);
405
- formData.append('commit_message', document.getElementById('commit_message').value);
406
-
407
- // Add files to form data
408
- selectedFiles.forEach(file => {
409
- formData.append('files', file);
410
- });
411
-
412
- // Show progress
413
- document.getElementById('progressContainer').style.display = 'block';
414
- document.getElementById('uploadBtn').disabled = true;
415
- document.getElementById('uploadBtn').textContent = 'Uploading...';
416
-
417
- fetch('/upload', {
418
- method: 'POST',
419
- body: formData
420
- })
421
- .then(response => response.json())
422
- .then(data => {
423
- if (data.success) {
424
- showMessage(data.message, 'success');
425
- checkProgress(data.upload_id);
426
- } else {
427
- showMessage(data.error || 'Upload failed', 'error');
428
- resetForm();
429
- }
430
- })
431
- .catch(error => {
432
- showMessage('Error: ' + error.message, 'error');
433
- resetForm();
434
- });
435
- });
436
-
437
- function checkProgress(uploadId) {
438
- const progressFill = document.getElementById('progressFill');
439
- const progressText = document.getElementById('progressText');
440
-
441
- const interval = setInterval(() => {
442
- fetch(`/progress/${uploadId}`)
443
- .then(response => response.json())
444
- .then(data => {
445
- progressFill.style.width = data.progress + '%';
446
- progressText.textContent = data.status;
447
-
448
- if (data.completed || data.progress === 100) {
449
- clearInterval(interval);
450
- if (data.status.includes('Error')) {
451
- showMessage(data.status, 'error');
452
- } else {
453
- showMessage('Upload completed successfully! 🎉', 'success');
454
- }
455
- resetForm();
456
- }
457
- })
458
- .catch(error => {
459
- clearInterval(interval);
460
- showMessage('Error checking progress: ' + error.message, 'error');
461
- resetForm();
462
- });
463
- }, 2000);
464
- }
465
-
466
- function showMessage(message, type) {
467
- const messageDiv = document.getElementById('message');
468
- messageDiv.className = type;
469
- messageDiv.textContent = message;
470
- messageDiv.style.display = 'block';
471
- }
472
-
473
- function resetForm() {
474
- document.getElementById('uploadBtn').disabled = false;
475
- document.getElementById('uploadBtn').textContent = '🚀 Upload to HuggingFace';
476
- document.getElementById('progressContainer').style.display = 'none';
477
- document.getElementById('progressFill').style.width = '0%';
478
- }
479
- </script>
480
- </body>
481
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
huggingface-uploader/templates/index2.html DELETED
@@ -1,962 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>🤗 HuggingFace Uploader Pro</title>
7
- <style>
8
- * {
9
- margin: 0;
10
- padding: 0;
11
- box-sizing: border-box;
12
- }
13
-
14
- body {
15
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
16
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
17
- min-height: 100vh;
18
- padding: 20px;
19
- }
20
-
21
- .container {
22
- max-width: 900px;
23
- margin: 0 auto;
24
- background: white;
25
- border-radius: 20px;
26
- box-shadow: 0 20px 40px rgba(0,0,0,0.1);
27
- overflow: hidden;
28
- }
29
-
30
- .header {
31
- background: linear-gradient(135deg, #ff6b6b 0%, #feca57 100%);
32
- color: white;
33
- padding: 30px;
34
- text-align: center;
35
- }
36
-
37
- .header h1 {
38
- font-size: 2.5em;
39
- margin-bottom: 10px;
40
- text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
41
- }
42
-
43
- .header p {
44
- font-size: 1.1em;
45
- opacity: 0.9;
46
- }
47
-
48
- .content {
49
- padding: 40px;
50
- }
51
-
52
- .tabs {
53
- display: flex;
54
- margin-bottom: 30px;
55
- border-bottom: 2px solid #f0f0f0;
56
- }
57
-
58
- .tab {
59
- padding: 15px 25px;
60
- cursor: pointer;
61
- border: none;
62
- background: none;
63
- font-size: 16px;
64
- font-weight: 600;
65
- color: #666;
66
- border-bottom: 3px solid transparent;
67
- transition: all 0.3s ease;
68
- }
69
-
70
- .tab.active {
71
- color: #007bff;
72
- border-bottom-color: #007bff;
73
- }
74
-
75
- .tab:hover {
76
- background-color: #f8f9fa;
77
- }
78
-
79
- .tab-content {
80
- display: none;
81
- }
82
-
83
- .tab-content.active {
84
- display: block;
85
- }
86
-
87
- .form-group {
88
- margin-bottom: 25px;
89
- }
90
-
91
- .form-row {
92
- display: flex;
93
- gap: 20px;
94
- }
95
-
96
- .form-row .form-group {
97
- flex: 1;
98
- }
99
-
100
- label {
101
- display: block;
102
- margin-bottom: 8px;
103
- font-weight: 600;
104
- color: #333;
105
- font-size: 14px;
106
- }
107
-
108
- input, select, textarea {
109
- width: 100%;
110
- padding: 12px 16px;
111
- border: 2px solid #e1e5e9;
112
- border-radius: 8px;
113
- font-size: 14px;
114
- transition: all 0.3s ease;
115
- background-color: #fff;
116
- }
117
-
118
- input:focus, select:focus, textarea:focus {
119
- outline: none;
120
- border-color: #007bff;
121
- box-shadow: 0 0 0 3px rgba(0,123,255,0.1);
122
- }
123
-
124
- .checkbox-group {
125
- display: flex;
126
- align-items: center;
127
- gap: 10px;
128
- }
129
-
130
- .checkbox-group input[type="checkbox"] {
131
- width: auto;
132
- margin: 0;
133
- }
134
-
135
- .file-upload-section {
136
- background: #f8f9fa;
137
- border-radius: 12px;
138
- padding: 30px;
139
- margin: 25px 0;
140
- border: 2px dashed #dee2e6;
141
- transition: all 0.3s ease;
142
- }
143
-
144
- .file-upload-section.drag-over {
145
- border-color: #007bff;
146
- background-color: #e6f3ff;
147
- transform: scale(1.02);
148
- }
149
-
150
- .upload-modes {
151
- display: flex;
152
- gap: 15px;
153
- margin-bottom: 25px;
154
- }
155
-
156
- .upload-mode {
157
- flex: 1;
158
- padding: 20px;
159
- border: 2px solid #e1e5e9;
160
- border-radius: 12px;
161
- text-align: center;
162
- cursor: pointer;
163
- transition: all 0.3s ease;
164
- background: white;
165
- }
166
-
167
- .upload-mode.active {
168
- border-color: #007bff;
169
- background: linear-gradient(135deg, #007bff 0%, #0056b3 100%);
170
- color: white;
171
- transform: translateY(-2px);
172
- box-shadow: 0 4px 12px rgba(0,123,255,0.3);
173
- }
174
-
175
- .upload-mode:hover:not(.active) {
176
- border-color: #007bff;
177
- transform: translateY(-1px);
178
- }
179
-
180
- .upload-mode .icon {
181
- font-size: 2em;
182
- margin-bottom: 10px;
183
- }
184
-
185
- .upload-mode .title {
186
- font-weight: 600;
187
- margin-bottom: 5px;
188
- }
189
-
190
- .upload-mode .description {
191
- font-size: 12px;
192
- opacity: 0.8;
193
- }
194
-
195
- .file-input-container {
196
- text-align: center;
197
- padding: 40px;
198
- background: white;
199
- border-radius: 12px;
200
- border: 2px dashed #dee2e6;
201
- transition: all 0.3s ease;
202
- }
203
-
204
- .file-input-container:hover {
205
- border-color: #007bff;
206
- background-color: #f8f9ff;
207
- }
208
-
209
- .file-input-label {
210
- display: inline-block;
211
- background: linear-gradient(135deg, #007bff 0%, #0056b3 100%);
212
- color: white;
213
- padding: 15px 30px;
214
- border-radius: 50px;
215
- cursor: pointer;
216
- font-weight: 600;
217
- transition: all 0.3s ease;
218
- margin-bottom: 15px;
219
- }
220
-
221
- .file-input-label:hover {
222
- transform: translateY(-2px);
223
- box-shadow: 0 4px 12px rgba(0,123,255,0.3);
224
- }
225
-
226
- .file-list {
227
- margin-top: 20px;
228
- max-height: 300px;
229
- overflow-y: auto;
230
- background: white;
231
- border-radius: 8px;
232
- border: 1px solid #e1e5e9;
233
- }
234
-
235
- .file-item {
236
- display: flex;
237
- justify-content: space-between;
238
- align-items: center;
239
- padding: 12px 16px;
240
- border-bottom: 1px solid #f0f0f0;
241
- transition: background-color 0.2s ease;
242
- }
243
-
244
- .file-item:last-child {
245
- border-bottom: none;
246
- }
247
-
248
- .file-item:hover {
249
- background-color: #f8f9fa;
250
- }
251
-
252
- .file-info {
253
- display: flex;
254
- align-items: center;
255
- gap: 10px;
256
- }
257
-
258
- .file-icon {
259
- font-size: 1.2em;
260
- }
261
-
262
- .file-name {
263
- font-weight: 500;
264
- color: #333;
265
- }
266
-
267
- .file-size {
268
- color: #666;
269
- font-size: 0.9em;
270
- }
271
-
272
- .remove-file {
273
- background: #ff4757;
274
- color: white;
275
- border: none;
276
- border-radius: 50%;
277
- width: 24px;
278
- height: 24px;
279
- cursor: pointer;
280
- font-size: 12px;
281
- transition: all 0.2s ease;
282
- }
283
-
284
- .remove-file:hover {
285
- background: #ff3742;
286
- transform: scale(1.1);
287
- }
288
-
289
- .upload-btn {
290
- background: linear-gradient(135deg, #28a745 0%, #20c997 100%);
291
- color: white;
292
- border: none;
293
- padding: 15px 40px;
294
- border-radius: 50px;
295
- font-size: 16px;
296
- font-weight: 600;
297
- cursor: pointer;
298
- transition: all 0.3s ease;
299
- width: 100%;
300
- margin-top: 20px;
301
- }
302
-
303
- .upload-btn:hover:not(:disabled) {
304
- transform: translateY(-2px);
305
- box-shadow: 0 4px 12px rgba(40,167,69,0.3);
306
- }
307
-
308
- .upload-btn:disabled {
309
- opacity: 0.6;
310
- cursor: not-allowed;
311
- }
312
-
313
- .progress-container {
314
- margin-top: 20px;
315
- display: none;
316
- }
317
-
318
- .progress-bar {
319
- width: 100%;
320
- height: 20px;
321
- background: #e1e5e9;
322
- border-radius: 10px;
323
- overflow: hidden;
324
- position: relative;
325
- }
326
-
327
- .progress-fill {
328
- height: 100%;
329
- background: linear-gradient(90deg, #007bff 0%, #0056b3 100%);
330
- width: 0%;
331
- transition: width 0.3s ease;
332
- position: relative;
333
- }
334
-
335
- .progress-text {
336
- position: absolute;
337
- top: 50%;
338
- left: 50%;
339
- transform: translate(-50%, -50%);
340
- color: white;
341
- font-weight: 600;
342
- font-size: 12px;
343
- text-shadow: 1px 1px 2px rgba(0,0,0,0.5);
344
- }
345
-
346
- .status-text {
347
- margin-top: 10px;
348
- text-align: center;
349
- color: #666;
350
- font-size: 14px;
351
- }
352
-
353
- .alert {
354
- padding: 15px 20px;
355
- border-radius: 8px;
356
- margin-bottom: 20px;
357
- font-weight: 500;
358
- }
359
-
360
- .alert-success {
361
- background: #d4edda;
362
- color: #155724;
363
- border: 1px solid #c3e6cb;
364
- }
365
-
366
- .alert-error {
367
- background: #f8d7da;
368
- color: #721c24;
369
- border: 1px solid #f5c6cb;
370
- }
371
-
372
- .alert-info {
373
- background: #d1ecf1;
374
- color: #0c5460;
375
- border: 1px solid #bee5eb;
376
- }
377
-
378
- .history-item {
379
- background: #f8f9fa;
380
- border-radius: 8px;
381
- padding: 15px;
382
- margin-bottom: 15px;
383
- border-left: 4px solid #007bff;
384
- }
385
-
386
- .history-item.success {
387
- border-left-color: #28a745;
388
- }
389
-
390
- .history-item.error {
391
- border-left-color: #dc3545;
392
- }
393
-
394
- .history-header {
395
- display: flex;
396
- justify-content: space-between;
397
- align-items: center;
398
- margin-bottom: 8px;
399
- }
400
-
401
- .history-repo {
402
- font-weight: 600;
403
- color: #333;
404
- }
405
-
406
- .history-time {
407
- color: #666;
408
- font-size: 0.9em;
409
- }
410
-
411
- .history-status {
412
- display: flex;
413
- align-items: center;
414
- gap: 5px;
415
- }
416
-
417
- .status-badge {
418
- padding: 4px 8px;
419
- border-radius: 12px;
420
- font-size: 0.8em;
421
- font-weight: 600;
422
- }
423
-
424
- .status-badge.success {
425
- background: #d4edda;
426
- color: #155724;
427
- }
428
-
429
- .status-badge.error {
430
- background: #f8d7da;
431
- color: #721c24;
432
- }
433
-
434
- .help-text {
435
- font-size: 0.9em;
436
- color: #666;
437
- margin-top: 5px;
438
- }
439
-
440
- .help-section {
441
- margin-bottom: 30px;
442
- }
443
-
444
- .help-section h4 {
445
- color: #333;
446
- margin-bottom: 15px;
447
- font-size: 1.2em;
448
- }
449
-
450
- .help-section ul, .help-section ol {
451
- margin-left: 20px;
452
- margin-bottom: 15px;
453
- }
454
-
455
- .help-section li {
456
- margin-bottom: 8px;
457
- line-height: 1.5;
458
- }
459
-
460
- .help-section code {
461
- background: #f8f9fa;
462
- padding: 2px 6px;
463
- border-radius: 4px;
464
- font-family: 'Courier New', monospace;
465
- font-size: 0.9em;
466
- }
467
-
468
- .help-section a {
469
- color: #007bff;
470
- text-decoration: none;
471
- }
472
-
473
- .help-section a:hover {
474
- text-decoration: underline;
475
- }
476
-
477
- .hidden {
478
- display: none;
479
- }
480
-
481
- @media (max-width: 768px) {
482
- .form-row {
483
- flex-direction: column;
484
- }
485
-
486
- .upload-modes {
487
- flex-direction: column;
488
- }
489
-
490
- .tabs {
491
- flex-wrap: wrap;
492
- }
493
-
494
- .tab {
495
- flex: 1;
496
- min-width: 120px;
497
- }
498
-
499
- .container {
500
- margin: 10px;
501
- }
502
-
503
- .content {
504
- padding: 20px;
505
- }
506
- }
507
- </style>
508
- </head>
509
- <body>
510
- <div class="container">
511
- <div class="header">
512
- <h1>🤗 HuggingFace Uploader Pro</h1>
513
- <p>Upload your files and folders to HuggingFace Hub with ease</p>
514
- </div>
515
-
516
- <div class="content">
517
- <div class="tabs">
518
- <button class="tab active" onclick="showTab('upload')">📤 Upload</button>
519
- <button class="tab" onclick="showTab('history')">📊 History</button>
520
- <button class="tab" onclick="showTab('help')">❓ Help</button>
521
- </div>
522
-
523
- <div id="upload" class="tab-content active">
524
- <form id="uploadForm" enctype="multipart/form-data">
525
- <div class="form-row">
526
- <div class="form-group">
527
- <label for="hf_token">HuggingFace Token *</label>
528
- <input type="password" id="hf_token" name="hf_token" required
529
- placeholder="hf_xxxxxxxxxxxx">
530
- <div class="help-text">Get your token from <a href="https://huggingface.co/settings/tokens" target="_blank">HuggingFace Settings</a></div>
531
- </div>
532
- <div class="form-group">
533
- <label for="repo_id">Repository ID *</label>
534
- <input type="text" id="repo_id" name="repo_id" required
535
- placeholder="username/repository-name">
536
- <div class="help-text">Format: username/repository-name</div>
537
- </div>
538
- </div>
539
-
540
- <div class="form-row">
541
- <div class="form-group">
542
- <label for="repo_type">Repository Type</label>
543
- <select id="repo_type" name="repo_type">
544
- <option value="space">Space</option>
545
- <option value="model">Model</option>
546
- <option value="dataset">Dataset</option>
547
- </select>
548
- </div>
549
- <div class="form-group">
550
- <label for="commit_message">Commit Message</label>
551
- <input type="text" id="commit_message" name="commit_message"
552
- placeholder="Upload via HuggingFace Uploader Pro">
553
- </div>
554
- </div>
555
-
556
- <div class="form-group">
557
- <div class="checkbox-group">
558
- <input type="checkbox" id="create_repo" name="create_repo">
559
- <label for="create_repo">Create repository if it doesn't exist</label>
560
- </div>
561
- </div>
562
-
563
- <div class="file-upload-section" id="dropZone">
564
- <div class="upload-modes">
565
- <div class="upload-mode active" id="fileMode" onclick="setUploadMode('file')">
566
- <div class="icon">📄</div>
567
- <div class="title">Files</div>
568
- <div class="description">Upload individual files or archives</div>
569
- </div>
570
- <div class="upload-mode" id="folderMode" onclick="setUploadMode('folder')">
571
- <div class="icon">📁</div>
572
- <div class="title">Folder</div>
573
- <div class="description">Upload entire folder structure</div>
574
- </div>
575
- </div>
576
-
577
- <div class="file-input-container">
578
- <label for="fileInput" class="file-input-label">
579
- <span id="uploadModeText">📤 Choose Files</span>
580
- </label>
581
- <input type="file" id="fileInput" name="files" multiple style="display: none;">
582
- <div id="dragText">or drag and drop files here</div>
583
- </div>
584
-
585
- <div id="fileList" class="file-list hidden"></div>
586
- </div>
587
-
588
- <button type="submit" class="upload-btn" id="uploadBtn">
589
- 🚀 Upload to HuggingFace
590
- </button>
591
-
592
- <div class="progress-container" id="progressContainer">
593
- <div class="progress-bar">
594
- <div class="progress-fill" id="progressFill">
595
- <div class="progress-text" id="progressText">0%</div>
596
- </div>
597
- </div>
598
- <div class="status-text" id="statusText">Preparing upload...</div>
599
- </div>
600
- </form>
601
-
602
- <div id="alertContainer"></div>
603
- </div>
604
-
605
- <div id="history" class="tab-content">
606
- <h3>Upload History</h3>
607
- <div id="historyList">
608
- <div class="alert-info">No upload history available.</div>
609
- </div>
610
- </div>
611
-
612
- <div id="help" class="tab-content">
613
- <div class="help-section">
614
- <h4>🚀 Getting Started</h4>
615
- <ol>
616
- <li>Get your HuggingFace token from <a href="https://huggingface.co/settings/tokens" target="_blank">Settings</a></li>
617
- <li>Enter your repository ID in the format: <code>username/repository-name</code></li>
618
- <li>Select the repository type (Space, Model, or Dataset)</li>
619
- <li>Choose your files or folders to upload</li>
620
- <li>Click "Upload to HuggingFace" and wait for completion</li>
621
- </ol>
622
- </div>
623
-
624
- <div class="help-section">
625
- <h4>📁 Supported File Types</h4>
626
- <ul>
627
- <li><strong>Code:</strong> .py, .js, .html, .css, .json, .md, .yaml, .yml, .toml, .ini, .cfg</li>
628
- <li><strong>Documents:</strong> .txt, .pdf, .docx, .csv, .xml</li>
629
- <li><strong>Images:</strong> .png, .jpg, .jpeg, .gif, .svg, .ico, .webp</li>
630
- <li><strong>Media:</strong> .mp4, .webm, .ogg, .mp3, .wav, .flac, .aac</li>
631
- <li><strong>Archives:</strong> .zip, .tar, .gz, .tgz (automatically extracted)</li>
632
- <li><strong>ML Models:</strong> .pkl, .h5, .hdf5, .pt, .pth, .onnx, .pb, .tflite, .safetensors</li>
633
- <li><strong>Notebooks:</strong> .ipynb</li>
634
- <li><strong>Fonts:</strong> .woff, .woff2, .ttf, .otf, .eot</li>
635
- </ul>
636
- </div>
637
-
638
- <div class="help-section">
639
- <h4>🔧 Features</h4>
640
- <ul>
641
- <li><strong>Drag & Drop:</strong> Simply drag files into the upload area</li>
642
- <li><strong>Folder Upload:</strong> Maintain directory structure when uploading folders</li>
643
- <li><strong>Archive Extraction:</strong> ZIP and TAR files are automatically extracted</li>
644
- <li><strong>Progress Tracking:</strong> Real-time upload progress and status</li>
645
- <li><strong>Auto Repository Creation:</strong> Create new repositories on the fly</li>
646
- <li><strong>Upload History:</strong> Track all your uploads</li>
647
- </ul>
648
- </div>
649
-
650
- <div class="help-section">
651
- <h4>🛠️ Tips</h4>
652
- <ul>
653
- <li>Use descriptive commit messages for better version control</li>
654
- <li>Large files may take longer to upload - be patient!</li>
655
- <li>Check the file type restrictions before uploading</li>
656
- <li>Repository names should be lowercase with hyphens</li>
657
- <li>Make sure your HuggingFace token has write permissions</li>
658
- </ul>
659
- </div>
660
- </div>
661
- </div>
662
- </div>
663
-
664
- <script>
665
- let selectedFiles = [];
666
- let uploadMode = 'file';
667
-
668
- // Tab switching
669
- function showTab(tabName) {
670
- // Hide all tabs
671
- document.querySelectorAll('.tab-content').forEach(tab => {
672
- tab.classList.remove('active');
673
- });
674
- document.querySelectorAll('.tab').forEach(tab => {
675
- tab.classList.remove('active');
676
- });
677
-
678
- // Show selected tab
679
- document.getElementById(tabName).classList.add('active');
680
- event.target.classList.add('active');
681
-
682
- // Load history if history tab is selected
683
- if (tabName === 'history') {
684
- loadHistory();
685
- }
686
- }
687
-
688
- // Upload mode switching
689
- function setUploadMode(mode) {
690
- uploadMode = mode;
691
- document.querySelectorAll('.upload-mode').forEach(el => {
692
- el.classList.remove('active');
693
- });
694
- document.getElementById(mode + 'Mode').classList.add('active');
695
-
696
- const fileInput = document.getElementById('fileInput');
697
- const modeText = document.getElementById('uploadModeText');
698
-
699
- if (mode === 'folder') {
700
- fileInput.setAttribute('webkitdirectory', '');
701
- fileInput.setAttribute('directory', '');
702
- modeText.textContent = '📁 Choose Folder';
703
- } else {
704
- fileInput.removeAttribute('webkitdirectory');
705
- fileInput.removeAttribute('directory');
706
- modeText.textContent = '📤 Choose Files';
707
- }
708
-
709
- // Clear selected files when switching modes
710
- selectedFiles = [];
711
- updateFileList();
712
- }
713
-
714
- // Drag and drop functionality
715
- const dropZone = document.getElementById('dropZone');
716
- const fileInput = document.getElementById('fileInput');
717
-
718
- dropZone.addEventListener('dragover', (e) => {
719
- e.preventDefault();
720
- dropZone.classList.add('drag-over');
721
- });
722
-
723
- dropZone.addEventListener('dragleave', (e) => {
724
- e.preventDefault();
725
- dropZone.classList.remove('drag-over');
726
- });
727
-
728
- dropZone.addEventListener('drop', (e) => {
729
- e.preventDefault();
730
- dropZone.classList.remove('drag-over');
731
-
732
- const files = Array.from(e.dataTransfer.files);
733
- handleFiles(files);
734
- });
735
-
736
- fileInput.addEventListener('change', (e) => {
737
- const files = Array.from(e.target.files);
738
- handleFiles(files);
739
- });
740
-
741
- function handleFiles(files) {
742
- selectedFiles = files;
743
- updateFileList();
744
- }
745
-
746
- function updateFileList() {
747
- const fileList = document.getElementById('fileList');
748
-
749
- if (selectedFiles.length === 0) {
750
- fileList.classList.add('hidden');
751
- return;
752
- }
753
-
754
- fileList.classList.remove('hidden');
755
- fileList.innerHTML = '';
756
-
757
- selectedFiles.forEach((file, index) => {
758
- const fileItem = document.createElement('div');
759
- fileItem.className = 'file-item';
760
-
761
- const fileIcon = getFileIcon(file.name);
762
- const fileSize = formatFileSize(file.size);
763
-
764
- fileItem.innerHTML = `
765
- <div class="file-info">
766
- <span class="file-icon">${fileIcon}</span>
767
- <span class="file-name">${file.name}</span>
768
- <span class="file-size">(${fileSize})</span>
769
- </div>
770
- <button type="button" class="remove-file" onclick="removeFile(${index})">×</button>
771
- `;
772
-
773
- fileList.appendChild(fileItem);
774
- });
775
- }
776
-
777
- function removeFile(index) {
778
- selectedFiles.splice(index, 1);
779
- updateFileList();
780
- }
781
-
782
- function getFileIcon(filename) {
783
- const ext = filename.split('.').pop().toLowerCase();
784
- const iconMap = {
785
- 'py': '🐍', 'js': '💛', 'html': '🌐', 'css': '🎨',
786
- 'json': '📋', 'md': '📝', 'txt': '📄', 'pdf': '📕',
787
- 'png': '🖼️', 'jpg': '🖼️', 'jpeg': '🖼️', 'gif': '🖼️',
788
- 'zip': '📦', 'tar': '📦', 'gz': '📦', 'tgz': '📦',
789
- 'ipynb': '📓', 'xlsx': '📊', 'csv': '📈',
790
- 'mp4': '🎬', 'mp3': '🎵', 'wav': '🎵'
791
- };
792
- return iconMap[ext] || '📄';
793
- }
794
-
795
- function formatFileSize(bytes) {
796
- if (bytes === 0) return '0 Bytes';
797
- const k = 1024;
798
- const sizes = ['Bytes', 'KB', 'MB', 'GB'];
799
- const i = Math.floor(Math.log(bytes) / Math.log(k));
800
- return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
801
- }
802
-
803
- // Form submission
804
- document.getElementById('uploadForm').addEventListener('submit', async (e) => {
805
- e.preventDefault();
806
-
807
- if (selectedFiles.length === 0) {
808
- showAlert('Please select files to upload', 'error');
809
- return;
810
- }
811
-
812
- const formData = new FormData();
813
- formData.append('hf_token', document.getElementById('hf_token').value);
814
- formData.append('repo_id', document.getElementById('repo_id').value);
815
- formData.append('repo_type', document.getElementById('repo_type').value);
816
- formData.append('commit_message', document.getElementById('commit_message').value);
817
- formData.append('create_repo', document.getElementById('create_repo').checked);
818
-
819
- selectedFiles.forEach(file => {
820
- formData.append('files', file);
821
- });
822
-
823
- const uploadBtn = document.getElementById('uploadBtn');
824
- const progressContainer = document.getElementById('progressContainer');
825
-
826
- uploadBtn.disabled = true;
827
- uploadBtn.textContent = '⏳ Uploading...';
828
- progressContainer.style.display = 'block';
829
-
830
- try {
831
- const response = await fetch('/upload', {
832
- method: 'POST',
833
- body: formData
834
- });
835
-
836
- const result = await response.json();
837
-
838
- if (response.ok) {
839
- showAlert('Upload started successfully!', 'success');
840
- pollProgress(result.upload_id);
841
- } else {
842
- throw new Error(result.error || 'Upload failed');
843
- }
844
- } catch (error) {
845
- showAlert(error.message, 'error');
846
- resetUploadForm();
847
- }
848
- });
849
-
850
- function pollProgress(uploadId) {
851
- const interval = setInterval(async () => {
852
- try {
853
- const response = await fetch(`/progress/${uploadId}`);
854
- const data = await response.json();
855
-
856
- updateProgress(data.progress, data.status);
857
-
858
- if (data.completed || data.status.toLowerCase().includes('error')) {
859
- clearInterval(interval);
860
- resetUploadForm();
861
-
862
- if (data.completed) {
863
- showAlert('Upload completed successfully!', 'success');
864
- } else {
865
- showAlert(data.status, 'error');
866
- }
867
- }
868
- } catch (error) {
869
- clearInterval(interval);
870
- resetUploadForm();
871
- showAlert('Failed to get upload progress', 'error');
872
- }
873
- }, 2000);
874
- }
875
-
876
- function updateProgress(progress, status) {
877
- const progressFill = document.getElementById('progressFill');
878
- const progressText = document.getElementById('progressText');
879
- const statusText = document.getElementById('statusText');
880
-
881
- progressFill.style.width = `${progress}%`;
882
- progressText.textContent = `${progress}%`;
883
- statusText.textContent = status;
884
- }
885
-
886
- function resetUploadForm() {
887
- const uploadBtn = document.getElementById('uploadBtn');
888
- const progressContainer = document.getElementById('progressContainer');
889
-
890
- uploadBtn.disabled = false;
891
- uploadBtn.textContent = '🚀 Upload to HuggingFace';
892
- progressContainer.style.display = 'none';
893
-
894
- // Reset progress
895
- updateProgress(0, 'Ready to upload');
896
- }
897
-
898
- function showAlert(message, type) {
899
- const alertContainer = document.getElementById('alertContainer');
900
- const alertDiv = document.createElement('div');
901
- alertDiv.className = `alert alert-${type}`;
902
- alertDiv.textContent = message;
903
-
904
- alertContainer.innerHTML = '';
905
- alertContainer.appendChild(alertDiv);
906
-
907
- // Auto-hide after 5 seconds
908
- setTimeout(() => {
909
- alertDiv.remove();
910
- }, 5000);
911
- }
912
-
913
- // Load upload history
914
- async function loadHistory() {
915
- try {
916
- const response = await fetch('/history');
917
- const data = await response.json();
918
- const historyList = document.getElementById('historyList');
919
-
920
- if (data.history.length === 0) {
921
- historyList.innerHTML = '<div class="alert-info">No upload history available.</div>';
922
- return;
923
- }
924
-
925
- historyList.innerHTML = '';
926
- data.history.forEach(item => {
927
- const historyItem = document.createElement('div');
928
- historyItem.className = `history-item ${item.status}`;
929
-
930
- const timestamp = new Date(item.timestamp).toLocaleString();
931
-
932
- historyItem.innerHTML = `
933
- <div class="history-header">
934
- <span class="history-repo">${item.repo_id}</span>
935
- <span class="history-time">${timestamp}</span>
936
- </div>
937
- <div class="history-status">
938
- <span class="status-badge ${item.status}">${item.status}</span>
939
- <span>${item.repo_type}</span>
940
- </div>
941
- <div class="history-message">${item.message}</div>
942
- `;
943
-
944
- historyList.appendChild(historyItem);
945
- });
946
- } catch (error) {
947
- document.getElementById('historyList').innerHTML =
948
- '<div class="alert-error">Failed to load history</div>';
949
- }
950
- }
951
-
952
- // Initialize
953
- document.addEventListener('DOMContentLoaded', () => {
954
- // Set default upload mode
955
- setUploadMode('file');
956
-
957
- // Load history on page load
958
- loadHistory();
959
- });
960
- </script>
961
- </body>
962
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
huggingface-uploader/test.py DELETED
@@ -1,50 +0,0 @@
1
- from huggingface_hub import HfApi
2
- import tempfile
3
- import os
4
-
5
- # Replace with your actual token
6
- HF_TOKEN = ""
7
- REPO_ID = "Alamgirapi/HuggingFace_Uploader"
8
-
9
- def test_token():
10
- try:
11
- # Initialize API
12
- api = HfApi(token=HF_TOKEN)
13
-
14
- # Test token validity
15
- print("Testing token validity...")
16
- user_info = api.whoami()
17
- print(f"Token valid for user: {user_info.get('name', 'Unknown')}")
18
-
19
- # Test if repo exists and is accessible
20
- print(f"Testing repository access: {REPO_ID}")
21
- repo_info = api.repo_info(repo_id=REPO_ID, repo_type="space")
22
- print(f"Repository accessible: {repo_info.id}")
23
-
24
- # Test upload with a simple file
25
- print("Testing file upload...")
26
- with tempfile.NamedTemporaryFile(mode='w', suffix='.txt', delete=False) as f:
27
- f.write("Test upload from Python script")
28
- temp_file = f.name
29
-
30
- try:
31
- from huggingface_hub import upload_file
32
- upload_file(
33
- path_or_fileobj=temp_file,
34
- path_in_repo="test_upload.txt",
35
- repo_id=REPO_ID,
36
- repo_type="space",
37
- token=HF_TOKEN,
38
- commit_message="Test upload"
39
- )
40
- print("✅ Upload successful!")
41
- finally:
42
- os.unlink(temp_file)
43
-
44
- except Exception as e:
45
- print(f"❌ Error: {str(e)}")
46
- import traceback
47
- traceback.print_exc()
48
-
49
- if __name__ == "__main__":
50
- test_token()