let selectedFiles = []; let completedFiles = []; // Initialize the application document.addEventListener('DOMContentLoaded', function () { setupEventListeners(); }); function setupEventListeners() { const uploadSection = document.getElementById('upload-section'); const fileInput = document.getElementById('file-input'); const processButton = document.getElementById('process-button'); const clearButton = document.getElementById('clear-button'); const downloadButton = document.getElementById('download-button'); // File upload events uploadSection.addEventListener('click', () => fileInput.click()); uploadSection.addEventListener('dragover', handleDragOver); uploadSection.addEventListener('dragleave', handleDragLeave); uploadSection.addEventListener('drop', handleDrop); fileInput.addEventListener('change', handleFileSelect); // Button events processButton.addEventListener('click', processButtonFn); clearButton.addEventListener('click', clearAllFiles); downloadButton.addEventListener('click', downloadAllFiles); } function showMessage(message, type = 'error') { const errorMsg = document.getElementById('error-message'); const successMsg = document.getElementById('success-message'); // Hide both messages first errorMsg.style.display = 'none'; successMsg.style.display = 'none'; if (type === 'error') { errorMsg.textContent = message; errorMsg.style.display = 'block'; } else { successMsg.textContent = message; successMsg.style.display = 'block'; } // Auto-hide after 5 seconds setTimeout(() => { errorMsg.style.display = 'none'; successMsg.style.display = 'none'; }, 5000); } function handleDragOver(e) { e.preventDefault(); e.currentTarget.classList.add('dragover'); } function handleDragLeave(e) { e.preventDefault(); e.currentTarget.classList.remove('dragover'); } function handleDrop(e) { e.preventDefault(); const uploadSection = e.currentTarget; uploadSection.classList.remove('dragover'); const files = Array.from(e.dataTransfer.files); processFiles(files); } function handleFileSelect(e) { const files = Array.from(e.target.files); processFiles(files); } function generateUniqueId(file, length = 12) { ext = "." + file.name.split(".")[file.name.split(".").length - 1] const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; return Array.from({ length }, () => chars[Math.floor(Math.random() * chars.length)]).join('') + ext; } function saveFileIdToStorage(id) { let fileIds = JSON.parse(localStorage.getItem('uploadedFileIds') || '[]'); if (!fileIds.includes(id)) { fileIds.push(id); localStorage.setItem('uploadedFileIds', JSON.stringify(fileIds)); } } function processFiles(files) { files.forEach((file, index) => { const id = generateUniqueId(file); // generate unique ID const fileData = { id: id, file: file, name: file.name, size: formatFileSize(file.size), status: 'ready', preview: null }; // Store file ID for potential server deletion later saveFileIdToStorage(id); // Create preview for images const reader = new FileReader(); reader.onload = (e) => { fileData.preview = e.target.result; updateFileCard(fileData.id); }; reader.readAsDataURL(file); selectedFiles.push(fileData); }); updateUI(); } function updateUI() { const filesPreview = document.getElementById('files-preview'); const processButton = document.getElementById('process-button'); if (selectedFiles.length > 0) { filesPreview.style.display = 'block'; processButton.disabled = false; renderFileCards(); } else { filesPreview.style.display = 'none'; processButton.disabled = true; } } function renderFileCards() { const filesGrid = document.getElementById('files-grid'); filesGrid.innerHTML = selectedFiles.map(file => `
${window.location.pathname == '/image/remove_metadata' && file.other_info ? ``: ``}
${file.preview ? `${file.name}` : '🖼️'}

${file.name}

${file.size}

${getStatusText(file.status)}
`).join(''); } function updateFileCard(fileId) { const file = selectedFiles.find(f => f.id === fileId); if (!file) return; const card = document.getElementById(`file-${fileId}`); if (!card) return; const preview = card.querySelector('.file-preview'); if (file.preview && preview) { preview.innerHTML = `${file.name}`; } } async function processButtonFn() { if (selectedFiles.length === 0) return; const progressSection = document.getElementById('progress-section'); const progressBar = document.getElementById('progress-bar'); const progressText = document.getElementById('progress-text'); const processButton = document.getElementById('process-button'); // Show progress section progressSection.style.display = 'block'; processButton.disabled = true; const old_message = processButton.innerHTML; processButton.innerHTML = '🔄 Processing...'; const settings = getSettingsData(); completedFiles = []; for (let i = 0; i < selectedFiles.length; i++) { const file = selectedFiles[i]; // Update file status file.status = 'uploading'; renderFileCards(); try { // Upload and process the file await upload(file); file.status = 'processing'; renderFileCards(); const processedFile = await secret_sauce(file, settings); file.status = 'completed'; file.other_info = processedFile.other_info; renderFileCards(); completedFiles.push(processedFile); } catch (error) { console.error('Processing error:', error); file.status = 'error'; showMessage(`Error processing ${file.name}: ${error.message}`, 'error'); } // Update progress const progress = Math.round(((i + 1) / selectedFiles.length) * 100); progressBar.style.width = progress + '%'; progressText.textContent = `${progress}% complete (${i + 1}/${selectedFiles.length})`; renderFileCards(); } // Completion processButton.disabled = false; processButton.innerHTML = old_message; const successfulProcessing = completedFiles.length; if (successfulProcessing > 0) { document.getElementById('download-button').style.display = 'inline-flex'; showMessage(`Successfully processed ${successfulProcessing} image(s)!`, 'success'); } setTimeout(() => { progressSection.style.display = 'none'; }, 1000); } async function secret_sauce(file, settings) { const formData = new FormData(); formData.append('id', file.id); for (let key of Object.keys(settings)) { if (settings[key]) { formData.append('to_format', settings[key]); } } try { const response = await fetch(secret_sauce_url(), { method: 'POST', body: formData }); if (!response.ok) { const errorData = await response.json().catch(() => ({})); throw new Error(errorData.message || `HTTP error! status: ${response.status}`); } const result = await response.json(); return { id: file.id, originalName: file.name, processedName: result.new_filename, url: "/image/download?id=" + result.new_filename, other_info: result.other_info }; } catch (error) { throw new Error(`Failed to process ${file.name}: ${error.message}`); } } function downloadAllFiles() { if (completedFiles.length === 0) return; // Download each completedFiles file completedFiles.forEach((file, index) => { setTimeout(() => { const link = document.createElement('a'); link.href = file.url; link.download = file.processedName; link.target = '_blank'; document.body.appendChild(link); link.click(); document.body.removeChild(link); }, index * 100); }); } 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]; } function removeFile(fileId) { selectedFiles = selectedFiles.filter(file => file.id !== fileId); completedFiles = completedFiles.filter(file => file.id !== fileId); updateUI(); if (selectedFiles.length === 0) { document.getElementById('download-button').style.display = 'none'; } } async function clearAllFiles() { const fileIds = JSON.parse(localStorage.getItem('uploadedFileIds') || '[]'); if (fileIds.length > 0) { try { await fetch('/image/delete', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ids: fileIds }) }); } catch (err) { console.error('Error sending delete request:', err); } } // Clear everything locally selectedFiles = []; completedFiles = []; updateUI(); document.getElementById('download-button').style.display = 'none'; document.getElementById('progress-section').style.display = 'none'; localStorage.removeItem('uploadedFileIds'); } async function upload(file) { const formData = new FormData(); formData.append('image', file.file); formData.append('id', file.id); try { const response = await fetch('/image/upload', { method: 'POST', body: formData }); if (!response.ok) { const errorData = await response.json().catch(() => ({})); throw new Error(errorData.message || `HTTP error! status: ${response.status}`); } return true; } catch (error) { throw new Error(`Failed to upload ${file.name}: ${error.message}`); } } function getSettingsData() { return { to_format: document.getElementById('output-format')?.value }; } function getStatusText(status) { switch (status) { case 'ready': return 'Ready'; case 'uploading': return 'Uploading...'; case 'processing': return window.location.pathname == '/image/remove_metadata' ? 'Removing metadata...' : 'Converting...'; case 'completed': return 'Processed'; case 'error': return 'Error'; default: return 'Unknown'; } } function showMetadata(fileId) { const file = selectedFiles.find(f => f.id === fileId); const contentEl = document.getElementById('metadata-content'); if (file && file.other_info) { contentEl.textContent = JSON.stringify(file.other_info, null, 2); document.getElementById('metadata-modal').style.display = 'block'; document.getElementById('modal-backdrop').style.display = 'block'; } else { contentEl.textContent = 'No metadata removed or unavailable.'; document.getElementById('metadata-modal').style.display = 'block'; document.getElementById('modal-backdrop').style.display = 'block'; } } function closeMetadataModal() { document.getElementById('metadata-modal').style.display = 'none'; document.getElementById('modal-backdrop').style.display = 'none'; }