Spaces:
Sleeping
Sleeping
| <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> |