Alamgirapi commited on
Commit
9228379
·
verified ·
1 Parent(s): 08dc85a

Upload via Flask App

Browse files
Files changed (1) hide show
  1. 20250709_000514_app.py +263 -0
20250709_000514_app.py ADDED
@@ -0,0 +1,263 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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'}
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
+ port = int(os.environ.get('PORT', 7860)) # Hugging Face Spaces uses port 7860
263
+ app.run(debug=False, host='0.0.0.0', port=port, use_reloader=False)