Alamgirapi's picture
Upload via Flask App
08dc85a verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>🤗 HuggingFace Uploader Pro</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 900px;
margin: 0 auto;
background: white;
border-radius: 20px;
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
overflow: hidden;
}
.header {
background: linear-gradient(135deg, #ff6b6b 0%, #feca57 100%);
color: white;
padding: 30px;
text-align: center;
}
.header h1 {
font-size: 2.5em;
margin-bottom: 10px;
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
}
.header p {
font-size: 1.1em;
opacity: 0.9;
}
.content {
padding: 40px;
}
.tabs {
display: flex;
margin-bottom: 30px;
border-bottom: 2px solid #f0f0f0;
}
.tab {
padding: 15px 25px;
cursor: pointer;
border: none;
background: none;
font-size: 16px;
font-weight: 600;
color: #666;
border-bottom: 3px solid transparent;
transition: all 0.3s ease;
}
.tab.active {
color: #007bff;
border-bottom-color: #007bff;
}
.tab:hover {
background-color: #f8f9fa;
}
.tab-content {
display: none;
}
.tab-content.active {
display: block;
}
.form-group {
margin-bottom: 25px;
}
.form-row {
display: flex;
gap: 20px;
}
.form-row .form-group {
flex: 1;
}
label {
display: block;
margin-bottom: 8px;
font-weight: 600;
color: #333;
font-size: 14px;
}
input, select, textarea {
width: 100%;
padding: 12px 16px;
border: 2px solid #e1e5e9;
border-radius: 8px;
font-size: 14px;
transition: all 0.3s ease;
background-color: #fff;
}
input:focus, select:focus, textarea:focus {
outline: none;
border-color: #007bff;
box-shadow: 0 0 0 3px rgba(0,123,255,0.1);
}
.checkbox-group {
display: flex;
align-items: center;
gap: 10px;
}
.checkbox-group input[type="checkbox"] {
width: auto;
margin: 0;
}
.file-upload-section {
background: #f8f9fa;
border-radius: 12px;
padding: 30px;
margin: 25px 0;
border: 2px dashed #dee2e6;
transition: all 0.3s ease;
}
.file-upload-section.drag-over {
border-color: #007bff;
background-color: #e6f3ff;
transform: scale(1.02);
}
.upload-modes {
display: flex;
gap: 15px;
margin-bottom: 25px;
}
.upload-mode {
flex: 1;
padding: 20px;
border: 2px solid #e1e5e9;
border-radius: 12px;
text-align: center;
cursor: pointer;
transition: all 0.3s ease;
background: white;
}
.upload-mode.active {
border-color: #007bff;
background: linear-gradient(135deg, #007bff 0%, #0056b3 100%);
color: white;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0,123,255,0.3);
}
.upload-mode:hover:not(.active) {
border-color: #007bff;
transform: translateY(-1px);
}
.upload-mode .icon {
font-size: 2em;
margin-bottom: 10px;
}
.upload-mode .title {
font-weight: 600;
margin-bottom: 5px;
}
.upload-mode .description {
font-size: 12px;
opacity: 0.8;
}
.file-input-container {
text-align: center;
padding: 40px;
background: white;
border-radius: 12px;
border: 2px dashed #dee2e6;
transition: all 0.3s ease;
}
.file-input-container:hover {
border-color: #007bff;
background-color: #f8f9ff;
}
.file-input-label {
display: inline-block;
background: linear-gradient(135deg, #007bff 0%, #0056b3 100%);
color: white;
padding: 15px 30px;
border-radius: 50px;
cursor: pointer;
font-weight: 600;
transition: all 0.3s ease;
margin-bottom: 15px;
}
.file-input-label:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0,123,255,0.3);
}
.file-list {
margin-top: 20px;
max-height: 300px;
overflow-y: auto;
background: white;
border-radius: 8px;
border: 1px solid #e1e5e9;
}
.file-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
border-bottom: 1px solid #f0f0f0;
transition: background-color 0.2s ease;
}
.file-item:last-child {
border-bottom: none;
}
.file-item:hover {
background-color: #f8f9fa;
}
.file-info {
display: flex;
align-items: center;
gap: 10px;
}
.file-icon {
font-size: 1.2em;
}
.file-name {
font-weight: 500;
color: #333;
}
.file-size {
color: #666;
font-size: 0.9em;
}
.remove-file {
background: #ff4757;
color: white;
border: none;
border-radius: 50%;
width: 24px;
height: 24px;
cursor: pointer;
font-size: 12px;
transition: all 0.2s ease;
}
.remove-file:hover {
background: #ff3742;
transform: scale(1.1);
}
.upload-btn {
background: linear-gradient(135deg, #28a745 0%, #20c997 100%);
color: white;
border: none;
padding: 15px 40px;
border-radius: 50px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
width: 100%;
margin-top: 20px;
}
.upload-btn:hover:not(:disabled) {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(40,167,69,0.3);
}
.upload-btn:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.progress-container {
margin-top: 20px;
display: none;
}
.progress-bar {
width: 100%;
height: 20px;
background: #e1e5e9;
border-radius: 10px;
overflow: hidden;
position: relative;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #007bff 0%, #0056b3 100%);
width: 0%;
transition: width 0.3s ease;
position: relative;
}
.progress-text {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: white;
font-weight: 600;
font-size: 12px;
text-shadow: 1px 1px 2px rgba(0,0,0,0.5);
}
.status-text {
margin-top: 10px;
text-align: center;
color: #666;
font-size: 14px;
}
.alert {
padding: 15px 20px;
border-radius: 8px;
margin-bottom: 20px;
font-weight: 500;
}
.alert-success {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.alert-error {
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.alert-info {
background: #d1ecf1;
color: #0c5460;
border: 1px solid #bee5eb;
}
.history-item {
background: #f8f9fa;
border-radius: 8px;
padding: 15px;
margin-bottom: 15px;
border-left: 4px solid #007bff;
}
.history-item.success {
border-left-color: #28a745;
}
.history-item.error {
border-left-color: #dc3545;
}
.history-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
}
.history-repo {
font-weight: 600;
color: #333;
}
.history-time {
color: #666;
font-size: 0.9em;
}
.history-status {
display: flex;
align-items: center;
gap: 5px;
}
.status-badge {
padding: 4px 8px;
border-radius: 12px;
font-size: 0.8em;
font-weight: 600;
}
.status-badge.success {
background: #d4edda;
color: #155724;
}
.status-badge.error {
background: #f8d7da;
color: #721c24;
}
.help-text {
font-size: 0.9em;
color: #666;
margin-top: 5px;
}
.help-section {
margin-bottom: 30px;
}
.help-section h4 {
color: #333;
margin-bottom: 15px;
font-size: 1.2em;
}
.help-section ul, .help-section ol {
margin-left: 20px;
margin-bottom: 15px;
}
.help-section li {
margin-bottom: 8px;
line-height: 1.5;
}
.help-section code {
background: #f8f9fa;
padding: 2px 6px;
border-radius: 4px;
font-family: 'Courier New', monospace;
font-size: 0.9em;
}
.help-section a {
color: #007bff;
text-decoration: none;
}
.help-section a:hover {
text-decoration: underline;
}
.hidden {
display: none;
}
@media (max-width: 768px) {
.form-row {
flex-direction: column;
}
.upload-modes {
flex-direction: column;
}
.tabs {
flex-wrap: wrap;
}
.tab {
flex: 1;
min-width: 120px;
}
.container {
margin: 10px;
}
.content {
padding: 20px;
}
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🤗 HuggingFace Uploader Pro</h1>
<p>Upload your files and folders to HuggingFace Hub with ease</p>
</div>
<div class="content">
<div class="tabs">
<button class="tab active" onclick="showTab('upload')">📤 Upload</button>
<button class="tab" onclick="showTab('history')">📊 History</button>
<button class="tab" onclick="showTab('help')">❓ Help</button>
</div>
<div id="upload" class="tab-content active">
<form id="uploadForm" enctype="multipart/form-data">
<div class="form-row">
<div class="form-group">
<label for="hf_token">HuggingFace Token *</label>
<input type="password" id="hf_token" name="hf_token" required
placeholder="hf_xxxxxxxxxxxx">
<div class="help-text">Get your token from <a href="https://huggingface.co/settings/tokens" target="_blank">HuggingFace Settings</a></div>
</div>
<div class="form-group">
<label for="repo_id">Repository ID *</label>
<input type="text" id="repo_id" name="repo_id" required
placeholder="username/repository-name">
<div class="help-text">Format: username/repository-name</div>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label for="repo_type">Repository Type</label>
<select id="repo_type" name="repo_type">
<option value="space">Space</option>
<option value="model">Model</option>
<option value="dataset">Dataset</option>
</select>
</div>
<div class="form-group">
<label for="commit_message">Commit Message</label>
<input type="text" id="commit_message" name="commit_message"
placeholder="Upload via HuggingFace Uploader Pro">
</div>
</div>
<div class="form-group">
<div class="checkbox-group">
<input type="checkbox" id="create_repo" name="create_repo">
<label for="create_repo">Create repository if it doesn't exist</label>
</div>
</div>
<div class="file-upload-section" id="dropZone">
<div class="upload-modes">
<div class="upload-mode active" id="fileMode" onclick="setUploadMode('file')">
<div class="icon">📄</div>
<div class="title">Files</div>
<div class="description">Upload individual files or archives</div>
</div>
<div class="upload-mode" id="folderMode" onclick="setUploadMode('folder')">
<div class="icon">📁</div>
<div class="title">Folder</div>
<div class="description">Upload entire folder structure</div>
</div>
</div>
<div class="file-input-container">
<label for="fileInput" class="file-input-label">
<span id="uploadModeText">📤 Choose Files</span>
</label>
<input type="file" id="fileInput" name="files" multiple style="display: none;">
<div id="dragText">or drag and drop files here</div>
</div>
<div id="fileList" class="file-list hidden"></div>
</div>
<button type="submit" class="upload-btn" id="uploadBtn">
🚀 Upload to HuggingFace
</button>
<div class="progress-container" id="progressContainer">
<div class="progress-bar">
<div class="progress-fill" id="progressFill">
<div class="progress-text" id="progressText">0%</div>
</div>
</div>
<div class="status-text" id="statusText">Preparing upload...</div>
</div>
</form>
<div id="alertContainer"></div>
</div>
<div id="history" class="tab-content">
<h3>Upload History</h3>
<div id="historyList">
<div class="alert-info">No upload history available.</div>
</div>
</div>
<div id="help" class="tab-content">
<div class="help-section">
<h4>🚀 Getting Started</h4>
<ol>
<li>Get your HuggingFace token from <a href="https://huggingface.co/settings/tokens" target="_blank">Settings</a></li>
<li>Enter your repository ID in the format: <code>username/repository-name</code></li>
<li>Select the repository type (Space, Model, or Dataset)</li>
<li>Choose your files or folders to upload</li>
<li>Click "Upload to HuggingFace" and wait for completion</li>
</ol>
</div>
<div class="help-section">
<h4>📁 Supported File Types</h4>
<ul>
<li><strong>Code:</strong> .py, .js, .html, .css, .json, .md, .yaml, .yml, .toml, .ini, .cfg</li>
<li><strong>Documents:</strong> .txt, .pdf, .docx, .csv, .xml</li>
<li><strong>Images:</strong> .png, .jpg, .jpeg, .gif, .svg, .ico, .webp</li>
<li><strong>Media:</strong> .mp4, .webm, .ogg, .mp3, .wav, .flac, .aac</li>
<li><strong>Archives:</strong> .zip, .tar, .gz, .tgz (automatically extracted)</li>
<li><strong>ML Models:</strong> .pkl, .h5, .hdf5, .pt, .pth, .onnx, .pb, .tflite, .safetensors</li>
<li><strong>Notebooks:</strong> .ipynb</li>
<li><strong>Fonts:</strong> .woff, .woff2, .ttf, .otf, .eot</li>
</ul>
</div>
<div class="help-section">
<h4>🔧 Features</h4>
<ul>
<li><strong>Drag & Drop:</strong> Simply drag files into the upload area</li>
<li><strong>Folder Upload:</strong> Maintain directory structure when uploading folders</li>
<li><strong>Archive Extraction:</strong> ZIP and TAR files are automatically extracted</li>
<li><strong>Progress Tracking:</strong> Real-time upload progress and status</li>
<li><strong>Auto Repository Creation:</strong> Create new repositories on the fly</li>
<li><strong>Upload History:</strong> Track all your uploads</li>
</ul>
</div>
<div class="help-section">
<h4>🛠️ Tips</h4>
<ul>
<li>Use descriptive commit messages for better version control</li>
<li>Large files may take longer to upload - be patient!</li>
<li>Check the file type restrictions before uploading</li>
<li>Repository names should be lowercase with hyphens</li>
<li>Make sure your HuggingFace token has write permissions</li>
</ul>
</div>
</div>
</div>
</div>
<script>
let selectedFiles = [];
let uploadMode = 'file';
// Tab switching
function showTab(tabName) {
// Hide all tabs
document.querySelectorAll('.tab-content').forEach(tab => {
tab.classList.remove('active');
});
document.querySelectorAll('.tab').forEach(tab => {
tab.classList.remove('active');
});
// Show selected tab
document.getElementById(tabName).classList.add('active');
event.target.classList.add('active');
// Load history if history tab is selected
if (tabName === 'history') {
loadHistory();
}
}
// Upload mode switching
function setUploadMode(mode) {
uploadMode = mode;
document.querySelectorAll('.upload-mode').forEach(el => {
el.classList.remove('active');
});
document.getElementById(mode + 'Mode').classList.add('active');
const fileInput = document.getElementById('fileInput');
const modeText = document.getElementById('uploadModeText');
if (mode === 'folder') {
fileInput.setAttribute('webkitdirectory', '');
fileInput.setAttribute('directory', '');
modeText.textContent = '📁 Choose Folder';
} else {
fileInput.removeAttribute('webkitdirectory');
fileInput.removeAttribute('directory');
modeText.textContent = '📤 Choose Files';
}
// Clear selected files when switching modes
selectedFiles = [];
updateFileList();
}
// Drag and drop functionality
const dropZone = document.getElementById('dropZone');
const fileInput = document.getElementById('fileInput');
dropZone.addEventListener('dragover', (e) => {
e.preventDefault();
dropZone.classList.add('drag-over');
});
dropZone.addEventListener('dragleave', (e) => {
e.preventDefault();
dropZone.classList.remove('drag-over');
});
dropZone.addEventListener('drop', (e) => {
e.preventDefault();
dropZone.classList.remove('drag-over');
const files = Array.from(e.dataTransfer.files);
handleFiles(files);
});
fileInput.addEventListener('change', (e) => {
const files = Array.from(e.target.files);
handleFiles(files);
});
function handleFiles(files) {
selectedFiles = files;
updateFileList();
}
function updateFileList() {
const fileList = document.getElementById('fileList');
if (selectedFiles.length === 0) {
fileList.classList.add('hidden');
return;
}
fileList.classList.remove('hidden');
fileList.innerHTML = '';
selectedFiles.forEach((file, index) => {
const fileItem = document.createElement('div');
fileItem.className = 'file-item';
const fileIcon = getFileIcon(file.name);
const fileSize = formatFileSize(file.size);
fileItem.innerHTML = `
<div class="file-info">
<span class="file-icon">${fileIcon}</span>
<span class="file-name">${file.name}</span>
<span class="file-size">(${fileSize})</span>
</div>
<button type="button" class="remove-file" onclick="removeFile(${index})">×</button>
`;
fileList.appendChild(fileItem);
});
}
function removeFile(index) {
selectedFiles.splice(index, 1);
updateFileList();
}
function getFileIcon(filename) {
const ext = filename.split('.').pop().toLowerCase();
const iconMap = {
'py': '🐍', 'js': '💛', 'html': '🌐', 'css': '🎨',
'json': '📋', 'md': '📝', 'txt': '📄', 'pdf': '📕',
'png': '🖼️', 'jpg': '🖼️', 'jpeg': '🖼️', 'gif': '🖼️',
'zip': '📦', 'tar': '📦', 'gz': '📦', 'tgz': '📦',
'ipynb': '📓', 'xlsx': '📊', 'csv': '📈',
'mp4': '🎬', 'mp3': '🎵', 'wav': '🎵'
};
return iconMap[ext] || '📄';
}
function formatFileSize(bytes) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
// Form submission
document.getElementById('uploadForm').addEventListener('submit', async (e) => {
e.preventDefault();
if (selectedFiles.length === 0) {
showAlert('Please select files to upload', 'error');
return;
}
const formData = new FormData();
formData.append('hf_token', document.getElementById('hf_token').value);
formData.append('repo_id', document.getElementById('repo_id').value);
formData.append('repo_type', document.getElementById('repo_type').value);
formData.append('commit_message', document.getElementById('commit_message').value);
formData.append('create_repo', document.getElementById('create_repo').checked);
selectedFiles.forEach(file => {
formData.append('files', file);
});
const uploadBtn = document.getElementById('uploadBtn');
const progressContainer = document.getElementById('progressContainer');
uploadBtn.disabled = true;
uploadBtn.textContent = '⏳ Uploading...';
progressContainer.style.display = 'block';
try {
const response = await fetch('/upload', {
method: 'POST',
body: formData
});
const result = await response.json();
if (response.ok) {
showAlert('Upload started successfully!', 'success');
pollProgress(result.upload_id);
} else {
throw new Error(result.error || 'Upload failed');
}
} catch (error) {
showAlert(error.message, 'error');
resetUploadForm();
}
});
function pollProgress(uploadId) {
const interval = setInterval(async () => {
try {
const response = await fetch(`/progress/${uploadId}`);
const data = await response.json();
updateProgress(data.progress, data.status);
if (data.completed || data.status.toLowerCase().includes('error')) {
clearInterval(interval);
resetUploadForm();
if (data.completed) {
showAlert('Upload completed successfully!', 'success');
} else {
showAlert(data.status, 'error');
}
}
} catch (error) {
clearInterval(interval);
resetUploadForm();
showAlert('Failed to get upload progress', 'error');
}
}, 2000);
}
function updateProgress(progress, status) {
const progressFill = document.getElementById('progressFill');
const progressText = document.getElementById('progressText');
const statusText = document.getElementById('statusText');
progressFill.style.width = `${progress}%`;
progressText.textContent = `${progress}%`;
statusText.textContent = status;
}
function resetUploadForm() {
const uploadBtn = document.getElementById('uploadBtn');
const progressContainer = document.getElementById('progressContainer');
uploadBtn.disabled = false;
uploadBtn.textContent = '🚀 Upload to HuggingFace';
progressContainer.style.display = 'none';
// Reset progress
updateProgress(0, 'Ready to upload');
}
function showAlert(message, type) {
const alertContainer = document.getElementById('alertContainer');
const alertDiv = document.createElement('div');
alertDiv.className = `alert alert-${type}`;
alertDiv.textContent = message;
alertContainer.innerHTML = '';
alertContainer.appendChild(alertDiv);
// Auto-hide after 5 seconds
setTimeout(() => {
alertDiv.remove();
}, 5000);
}
// Load upload history
async function loadHistory() {
try {
const response = await fetch('/history');
const data = await response.json();
const historyList = document.getElementById('historyList');
if (data.history.length === 0) {
historyList.innerHTML = '<div class="alert-info">No upload history available.</div>';
return;
}
historyList.innerHTML = '';
data.history.forEach(item => {
const historyItem = document.createElement('div');
historyItem.className = `history-item ${item.status}`;
const timestamp = new Date(item.timestamp).toLocaleString();
historyItem.innerHTML = `
<div class="history-header">
<span class="history-repo">${item.repo_id}</span>
<span class="history-time">${timestamp}</span>
</div>
<div class="history-status">
<span class="status-badge ${item.status}">${item.status}</span>
<span>${item.repo_type}</span>
</div>
<div class="history-message">${item.message}</div>
`;
historyList.appendChild(historyItem);
});
} catch (error) {
document.getElementById('historyList').innerHTML =
'<div class="alert-error">Failed to load history</div>';
}
}
// Initialize
document.addEventListener('DOMContentLoaded', () => {
// Set default upload mode
setUploadMode('file');
// Load history on page load
loadHistory();
});
</script>
</body>
</html>