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.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 = `
`;
}
}
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';
}