| |
| |
| |
| |
|
|
| |
|
|
| let rembgFile = null; |
|
|
| function initRemoveBackground() { |
| const dropZone = document.getElementById('rembgDropZone'); |
| const fileInput = document.getElementById('rembg_file'); |
| const btn = document.getElementById('btnRembg'); |
| const btnPreview = document.getElementById('btnRembgPreview'); |
| const notice = document.getElementById('rembgNotice'); |
|
|
| if (!dropZone || !fileInput) return; |
|
|
| |
| fetch('/api/rembg-status') |
| .then(r => r.json()) |
| .then(data => { |
| if (!data.available && notice) { |
| notice.style.display = 'flex'; |
| } |
| }) |
| .catch(() => {}); |
|
|
| initDropZone(dropZone, fileInput, { |
| maxSize: 50 * 1024 * 1024, |
| onFile: (file) => { |
| rembgFile = file; |
| btn.disabled = !file; |
| btnPreview.disabled = !file; |
| document.getElementById('rembgPreviewCard').style.display = 'none'; |
| if (file) showToast('Image Selected', file.name, 'success', 2000); |
| } |
| }); |
|
|
| btnPreview.addEventListener('click', () => previewRemoveBackground()); |
| btn.addEventListener('click', () => removeBackground()); |
| } |
|
|
| async function previewRemoveBackground() { |
| if (!rembgFile) { |
| showToast('No Image', 'Upload an image first', 'error'); |
| return; |
| } |
|
|
| const btn = document.getElementById('btnRembgPreview'); |
| const previewCard = document.getElementById('rembgPreviewCard'); |
| const originalImg = document.getElementById('rembgPreviewOriginal'); |
| const processedImg = document.getElementById('rembgPreviewProcessed'); |
|
|
| setButtonLoading(btn, true, 'Processing...'); |
|
|
| try { |
| const fd = new FormData(); |
| fd.append('file', rembgFile); |
|
|
| const res = await fetch('/api/preview/remove-background', { method: 'POST', body: fd }); |
|
|
| if (!res.ok) { |
| const err = await res.json().catch(() => ({ detail: 'Failed' })); |
| throw new Error(err.detail); |
| } |
|
|
| const data = await res.json(); |
| originalImg.src = 'data:image/png;base64,' + data.original; |
| processedImg.src = 'data:image/png;base64,' + data.processed; |
| previewCard.style.display = 'block'; |
| previewCard.scrollIntoView({ behavior: 'smooth', block: 'start' }); |
| |
| showToast('Preview Ready', 'Compare original vs processed', 'success', 2000); |
|
|
| } catch (e) { |
| showToast('Error', e.message, 'error'); |
| } finally { |
| setButtonLoading(btn, false); |
| } |
| } |
|
|
| async function removeBackground() { |
| if (!rembgFile) { |
| showToast('No Image', 'Upload an image first', 'error'); |
| return; |
| } |
|
|
| const btn = document.getElementById('btnRembg'); |
| const progress = document.getElementById('rembgProgress'); |
|
|
| setButtonLoading(btn, true, 'Processing...'); |
| showProgress(progress, true, true); |
|
|
| try { |
| const fd = new FormData(); |
| fd.append('file', rembgFile); |
| fd.append('output_name', document.getElementById('rembg_output').value || 'no-background.png'); |
|
|
| const res = await fetch('/api/remove-background', { method: 'POST', body: fd }); |
|
|
| if (!res.ok) { |
| const err = await res.json().catch(() => ({ detail: 'Failed' })); |
| throw new Error(err.detail); |
| } |
|
|
| const blob = await res.blob(); |
| const filename = document.getElementById('rembg_output').value || 'no-background.png'; |
| downloadBlob(blob, filename.endsWith('.png') ? filename : filename + '.png'); |
|
|
| showSuccessAnimation('Background Removed!', 'Download started'); |
|
|
| } catch (e) { |
| showToast('Error', e.message, 'error'); |
| } finally { |
| setButtonLoading(btn, false); |
| showProgress(progress, false); |
| } |
| } |
|
|
|
|
| |
|
|
| let imgWatermarkFile = null; |
|
|
| function initImageWatermark() { |
| const dropZone = document.getElementById('imgwatermarkDropZone'); |
| const fileInput = document.getElementById('imgwatermark_file'); |
| const btn = document.getElementById('btnImgWatermark'); |
| const btnPreview = document.getElementById('btnImgWatermarkPreview'); |
|
|
| if (!dropZone || !fileInput) return; |
|
|
| initDropZone(dropZone, fileInput, { |
| maxSize: 50 * 1024 * 1024, |
| onFile: (file) => { |
| imgWatermarkFile = file; |
| btn.disabled = !file; |
| btnPreview.disabled = !file; |
| document.getElementById('imgwatermarkPreviewCard').style.display = 'none'; |
| if (file) showToast('Image Selected', file.name, 'success', 2000); |
| } |
| }); |
|
|
| btnPreview.addEventListener('click', () => previewImageWatermark()); |
| btn.addEventListener('click', () => addImageWatermark()); |
| } |
|
|
| async function previewImageWatermark() { |
| if (!imgWatermarkFile) { |
| showToast('No Image', 'Upload an image first', 'error'); |
| return; |
| } |
|
|
| const text = document.getElementById('imgwatermark_text').value; |
| if (!text.trim()) { |
| showToast('No Text', 'Enter watermark text', 'error'); |
| return; |
| } |
|
|
| const btn = document.getElementById('btnImgWatermarkPreview'); |
| const previewCard = document.getElementById('imgwatermarkPreviewCard'); |
| const originalImg = document.getElementById('imgwatermarkPreviewOriginal'); |
| const processedImg = document.getElementById('imgwatermarkPreviewProcessed'); |
|
|
| setButtonLoading(btn, true, 'Loading...'); |
|
|
| try { |
| const fd = new FormData(); |
| fd.append('file', imgWatermarkFile); |
| fd.append('text', text); |
| fd.append('position', document.getElementById('imgwatermark_position').value); |
| fd.append('opacity', document.getElementById('imgwatermark_opacity').value); |
| fd.append('font_size', document.getElementById('imgwatermark_fontsize').value); |
| fd.append('color', document.getElementById('imgwatermark_color').value); |
|
|
| const res = await fetch('/api/preview/image-watermark', { method: 'POST', body: fd }); |
|
|
| if (!res.ok) { |
| const err = await res.json().catch(() => ({ detail: 'Failed' })); |
| throw new Error(err.detail); |
| } |
|
|
| const data = await res.json(); |
| originalImg.src = 'data:image/jpeg;base64,' + data.original; |
| processedImg.src = 'data:image/jpeg;base64,' + data.processed; |
| previewCard.style.display = 'block'; |
| previewCard.scrollIntoView({ behavior: 'smooth', block: 'start' }); |
| |
| showToast('Preview Ready', 'Compare original vs watermarked', 'success', 2000); |
|
|
| } catch (e) { |
| showToast('Error', e.message, 'error'); |
| } finally { |
| setButtonLoading(btn, false); |
| } |
| } |
|
|
| async function addImageWatermark() { |
| if (!imgWatermarkFile) { |
| showToast('No Image', 'Upload an image first', 'error'); |
| return; |
| } |
|
|
| const text = document.getElementById('imgwatermark_text').value; |
| if (!text.trim()) { |
| showToast('No Text', 'Enter watermark text', 'error'); |
| return; |
| } |
|
|
| const btn = document.getElementById('btnImgWatermark'); |
| const progress = document.getElementById('imgwatermarkProgress'); |
|
|
| setButtonLoading(btn, true, 'Processing...'); |
| showProgress(progress, true, true); |
|
|
| try { |
| const fd = new FormData(); |
| fd.append('file', imgWatermarkFile); |
| fd.append('text', text); |
| fd.append('position', document.getElementById('imgwatermark_position').value); |
| fd.append('opacity', document.getElementById('imgwatermark_opacity').value); |
| fd.append('font_size', document.getElementById('imgwatermark_fontsize').value); |
| fd.append('color', document.getElementById('imgwatermark_color').value); |
| fd.append('output_name', document.getElementById('imgwatermark_output').value || 'watermarked'); |
|
|
| const res = await fetch('/api/add-image-watermark', { method: 'POST', body: fd }); |
|
|
| if (!res.ok) { |
| const err = await res.json().catch(() => ({ detail: 'Failed' })); |
| throw new Error(err.detail); |
| } |
|
|
| const blob = await res.blob(); |
| const contentDisposition = res.headers.get('content-disposition'); |
| let filename = 'watermarked.jpg'; |
| if (contentDisposition) { |
| const match = contentDisposition.match(/filename="?([^"]+)"?/); |
| if (match) filename = match[1]; |
| } |
| downloadBlob(blob, filename); |
|
|
| showSuccessAnimation('Watermark Added!', 'Download started'); |
|
|
| } catch (e) { |
| showToast('Error', e.message, 'error'); |
| } finally { |
| setButtonLoading(btn, false); |
| showProgress(progress, false); |
| } |
| } |
|
|
|
|
| |
|
|
| let resizeFile = null; |
|
|
| function initResizeImage() { |
| const dropZone = document.getElementById('resizeDropZone'); |
| const fileInput = document.getElementById('resize_file'); |
| const btn = document.getElementById('btnResize'); |
| const btnPreview = document.getElementById('btnResizePreview'); |
| const presetSelect = document.getElementById('resize_preset'); |
| const widthInput = document.getElementById('resize_width'); |
| const heightInput = document.getElementById('resize_height'); |
|
|
| if (!dropZone || !fileInput) return; |
|
|
| initDropZone(dropZone, fileInput, { |
| maxSize: 50 * 1024 * 1024, |
| onFile: (file) => { |
| resizeFile = file; |
| btn.disabled = !file; |
| btnPreview.disabled = !file; |
| document.getElementById('resizePreviewCard').style.display = 'none'; |
| if (file) showToast('Image Selected', file.name, 'success', 2000); |
| } |
| }); |
|
|
| |
| presetSelect.addEventListener('change', () => { |
| const preset = presetSelect.value; |
| if (preset !== 'custom') { |
| widthInput.disabled = true; |
| heightInput.disabled = true; |
| widthInput.placeholder = 'Auto'; |
| heightInput.placeholder = 'Auto'; |
| } else { |
| widthInput.disabled = false; |
| heightInput.disabled = false; |
| } |
| }); |
|
|
| btnPreview.addEventListener('click', () => previewResizeImage()); |
| btn.addEventListener('click', () => resizeImage()); |
| } |
|
|
| async function previewResizeImage() { |
| if (!resizeFile) { |
| showToast('No Image', 'Upload an image first', 'error'); |
| return; |
| } |
|
|
| const btn = document.getElementById('btnResizePreview'); |
| const previewCard = document.getElementById('resizePreviewCard'); |
| const originalImg = document.getElementById('resizePreviewOriginal'); |
| const processedImg = document.getElementById('resizePreviewProcessed'); |
| const originalSizeSpan = document.getElementById('resizeOriginalSize'); |
| const newSizeSpan = document.getElementById('resizeNewSize'); |
|
|
| setButtonLoading(btn, true, 'Loading...'); |
|
|
| try { |
| const fd = new FormData(); |
| fd.append('file', resizeFile); |
| fd.append('preset', document.getElementById('resize_preset').value); |
| fd.append('width', document.getElementById('resize_width').value || '0'); |
| fd.append('height', document.getElementById('resize_height').value || '0'); |
| fd.append('maintain_aspect', document.getElementById('resize_aspect').checked); |
|
|
| const res = await fetch('/api/preview/resize-image', { method: 'POST', body: fd }); |
|
|
| if (!res.ok) { |
| const err = await res.json().catch(() => ({ detail: 'Failed' })); |
| throw new Error(err.detail); |
| } |
|
|
| const data = await res.json(); |
| originalImg.src = 'data:image/jpeg;base64,' + data.original; |
| processedImg.src = 'data:image/jpeg;base64,' + data.processed; |
| originalSizeSpan.textContent = `(${data.original_size})`; |
| newSizeSpan.textContent = `(${data.new_size})`; |
| previewCard.style.display = 'block'; |
| previewCard.scrollIntoView({ behavior: 'smooth', block: 'start' }); |
| |
| showToast('Preview Ready', `${data.original_size} → ${data.new_size}`, 'success', 2000); |
|
|
| } catch (e) { |
| showToast('Error', e.message, 'error'); |
| } finally { |
| setButtonLoading(btn, false); |
| } |
| } |
|
|
| async function resizeImage() { |
| if (!resizeFile) { |
| showToast('No Image', 'Upload an image first', 'error'); |
| return; |
| } |
|
|
| const btn = document.getElementById('btnResize'); |
| const progress = document.getElementById('resizeProgress'); |
|
|
| setButtonLoading(btn, true, 'Resizing...'); |
| showProgress(progress, true, true); |
|
|
| try { |
| const fd = new FormData(); |
| fd.append('file', resizeFile); |
| fd.append('preset', document.getElementById('resize_preset').value); |
| fd.append('width', document.getElementById('resize_width').value || '0'); |
| fd.append('height', document.getElementById('resize_height').value || '0'); |
| fd.append('maintain_aspect', document.getElementById('resize_aspect').checked); |
| fd.append('output_name', document.getElementById('resize_output').value || 'resized'); |
|
|
| const res = await fetch('/api/resize-image', { method: 'POST', body: fd }); |
|
|
| if (!res.ok) { |
| const err = await res.json().catch(() => ({ detail: 'Failed' })); |
| throw new Error(err.detail); |
| } |
|
|
| const blob = await res.blob(); |
| const contentDisposition = res.headers.get('content-disposition'); |
| let filename = 'resized.jpg'; |
| if (contentDisposition) { |
| const match = contentDisposition.match(/filename="?([^"]+)"?/); |
| if (match) filename = match[1]; |
| } |
| downloadBlob(blob, filename); |
|
|
| const newSize = res.headers.get('X-New-Size'); |
| showSuccessAnimation('Image Resized!', newSize ? `New size: ${newSize}` : 'Download started'); |
|
|
| } catch (e) { |
| showToast('Error', e.message, 'error'); |
| } finally { |
| setButtonLoading(btn, false); |
| showProgress(progress, false); |
| } |
| } |
|
|
|
|
| |
|
|
| let convertFile = null; |
|
|
| function initConvertImage() { |
| const dropZone = document.getElementById('convertDropZone'); |
| const fileInput = document.getElementById('convert_file'); |
| const btn = document.getElementById('btnConvert'); |
| const formatSelect = document.getElementById('convert_format'); |
| const qualityInput = document.getElementById('convert_quality'); |
|
|
| if (!dropZone || !fileInput) return; |
|
|
| initDropZone(dropZone, fileInput, { |
| maxSize: 50 * 1024 * 1024, |
| onFile: (file) => { |
| convertFile = file; |
| btn.disabled = !file; |
| if (file) { |
| showToast('Image Selected', file.name, 'success', 2000); |
| |
| updateConvertImageEstimate(); |
| } |
| } |
| }); |
|
|
| |
| let estimateTimeout = null; |
| const triggerEstimate = () => { |
| if (estimateTimeout) clearTimeout(estimateTimeout); |
| estimateTimeout = setTimeout(() => { |
| updateConvertImageEstimate(); |
| }, 300); |
| }; |
|
|
| formatSelect.addEventListener('change', triggerEstimate); |
| qualityInput.addEventListener('input', triggerEstimate); |
|
|
| btn.addEventListener('click', () => convertImage()); |
| } |
|
|
| async function updateConvertImageEstimate() { |
| if (!convertFile) return; |
| |
| const format = document.getElementById('convert_format').value; |
| const quality = document.getElementById('convert_quality').value; |
| |
| |
| let displayDiv = document.getElementById('convertEstimate'); |
| if (!displayDiv) { |
| displayDiv = document.createElement('div'); |
| displayDiv.id = 'convertEstimate'; |
| displayDiv.style.cssText = 'margin-top: 12px; padding: 12px; background: var(--border-light); border-radius: var(--radius); font-size: 13px;'; |
| const btnGroup = document.querySelector('#page-convert .btn-group'); |
| btnGroup.parentNode.insertBefore(displayDiv, btnGroup); |
| } |
| |
| displayDiv.innerHTML = '<span style="color: var(--text-muted);">Estimating file size...</span>'; |
| |
| try { |
| const fd = new FormData(); |
| fd.append('file', convertFile); |
| fd.append('target_format', format); |
| fd.append('quality', quality); |
| |
| const res = await fetch('/api/estimate/convert-image', { method: 'POST', body: fd }); |
| |
| if (!res.ok) { |
| displayDiv.innerHTML = '<span style="color: var(--text-muted);">Could not estimate</span>'; |
| return; |
| } |
| |
| const data = await res.json(); |
| const origKB = (data.original_size / 1024).toFixed(1); |
| const estKB = (data.estimated_size / 1024).toFixed(1); |
| const diff = data.estimated_size - data.original_size; |
| const diffPercent = ((diff / data.original_size) * 100).toFixed(1); |
| |
| let sizeChange = ''; |
| if (diff < 0) { |
| sizeChange = `<span style="color: var(--success); font-weight: 600;">${diffPercent}%</span>`; |
| } else if (diff > 0) { |
| sizeChange = `<span style="color: var(--warning); font-weight: 600;">+${diffPercent}%</span>`; |
| } else { |
| sizeChange = `<span style="color: var(--text-muted);">Same size</span>`; |
| } |
| |
| displayDiv.innerHTML = ` |
| <div style="display: flex; justify-content: space-between; align-items: center;"> |
| <span><strong>Original:</strong> ${origKB} KB</span> |
| <span style="color: var(--primary);">→</span> |
| <span><strong>${data.target_format}:</strong> ${estKB} KB</span> |
| ${sizeChange} |
| </div> |
| `; |
| } catch (e) { |
| displayDiv.innerHTML = '<span style="color: var(--text-muted);">Could not estimate</span>'; |
| } |
| } |
|
|
| async function convertImage() { |
| if (!convertFile) { |
| showToast('No Image', 'Upload an image first', 'error'); |
| return; |
| } |
|
|
| const btn = document.getElementById('btnConvert'); |
| const progress = document.getElementById('convertProgress'); |
|
|
| setButtonLoading(btn, true, 'Converting...'); |
| showProgress(progress, true, true); |
|
|
| try { |
| const fd = new FormData(); |
| fd.append('file', convertFile); |
| fd.append('target_format', document.getElementById('convert_format').value); |
| fd.append('quality', document.getElementById('convert_quality').value); |
| fd.append('output_name', document.getElementById('convert_output').value || 'converted'); |
|
|
| const res = await fetch('/api/convert-image', { method: 'POST', body: fd }); |
|
|
| if (!res.ok) { |
| const err = await res.json().catch(() => ({ detail: 'Failed' })); |
| throw new Error(err.detail); |
| } |
|
|
| const blob = await res.blob(); |
| const contentDisposition = res.headers.get('content-disposition'); |
| let filename = 'converted.jpg'; |
| if (contentDisposition) { |
| const match = contentDisposition.match(/filename="?([^"]+)"?/); |
| if (match) filename = match[1]; |
| } |
| downloadBlob(blob, filename); |
|
|
| const format = document.getElementById('convert_format').value.toUpperCase(); |
| showSuccessAnimation('Image Converted!', `Converted to ${format}`); |
|
|
| } catch (e) { |
| showToast('Error', e.message, 'error'); |
| } finally { |
| setButtonLoading(btn, false); |
| showProgress(progress, false); |
| } |
| } |
|
|
|
|
| |
|
|
| let compressImgFile = null; |
|
|
| function initCompressImage() { |
| const dropZone = document.getElementById('compressimgDropZone'); |
| const fileInput = document.getElementById('compressimg_file'); |
| const btn = document.getElementById('btnCompressImg'); |
| const btnPreview = document.getElementById('btnCompressImgPreview'); |
| const qualitySlider = document.getElementById('compressimg_quality'); |
| const qualityVal = document.getElementById('compressimg_quality_val'); |
|
|
| if (!dropZone || !fileInput) return; |
|
|
| initDropZone(dropZone, fileInput, { |
| maxSize: 50 * 1024 * 1024, |
| onFile: (file) => { |
| compressImgFile = file; |
| btn.disabled = !file; |
| btnPreview.disabled = !file; |
| document.getElementById('compressimgResult').style.display = 'none'; |
| document.getElementById('compressimgPreviewCard').style.display = 'none'; |
| if (file) { |
| showToast('Image Selected', file.name, 'success', 2000); |
| |
| updateCompressImageEstimate(); |
| } |
| } |
| }); |
|
|
| |
| let estimateTimeout = null; |
| qualitySlider.addEventListener('input', () => { |
| qualityVal.textContent = qualitySlider.value; |
| |
| if (estimateTimeout) clearTimeout(estimateTimeout); |
| estimateTimeout = setTimeout(() => { |
| updateCompressImageEstimate(); |
| }, 300); |
| }); |
|
|
| btnPreview.addEventListener('click', () => previewCompressImage()); |
| btn.addEventListener('click', () => compressImage()); |
| } |
|
|
| async function updateCompressImageEstimate() { |
| if (!compressImgFile) return; |
| |
| const estimateDiv = document.getElementById('compressimgEstimate'); |
| const quality = document.getElementById('compressimg_quality').value; |
| |
| |
| let displayDiv = estimateDiv; |
| if (!displayDiv) { |
| displayDiv = document.createElement('div'); |
| displayDiv.id = 'compressimgEstimate'; |
| displayDiv.style.cssText = 'margin-top: 12px; padding: 12px; background: var(--border-light); border-radius: var(--radius); font-size: 13px;'; |
| const qualityGroup = document.getElementById('compressimg_quality').closest('.form-group'); |
| qualityGroup.appendChild(displayDiv); |
| } |
| |
| displayDiv.innerHTML = '<span style="color: var(--text-muted);">Estimating file size...</span>'; |
| |
| try { |
| const fd = new FormData(); |
| fd.append('file', compressImgFile); |
| fd.append('quality', quality); |
| |
| const res = await fetch('/api/estimate/compress-image', { method: 'POST', body: fd }); |
| |
| if (!res.ok) { |
| displayDiv.innerHTML = '<span style="color: var(--text-muted);">Could not estimate</span>'; |
| return; |
| } |
| |
| const data = await res.json(); |
| const origKB = (data.original_size / 1024).toFixed(1); |
| const estKB = (data.estimated_size / 1024).toFixed(1); |
| const reduction = data.reduction_percent; |
| |
| displayDiv.innerHTML = ` |
| <div style="display: flex; justify-content: space-between; align-items: center;"> |
| <span><strong>Original:</strong> ${origKB} KB</span> |
| <span style="color: var(--primary);">→</span> |
| <span><strong>Estimated:</strong> ${estKB} KB</span> |
| <span style="color: ${reduction > 0 ? 'var(--success)' : 'var(--text-muted)'}; font-weight: 600;"> |
| ${reduction > 0 ? '-' + reduction + '%' : 'No reduction'} |
| </span> |
| </div> |
| `; |
| } catch (e) { |
| displayDiv.innerHTML = '<span style="color: var(--text-muted);">Could not estimate</span>'; |
| } |
| } |
|
|
| async function previewCompressImage() { |
| if (!compressImgFile) { |
| showToast('No Image', 'Upload an image first', 'error'); |
| return; |
| } |
|
|
| const btn = document.getElementById('btnCompressImgPreview'); |
| const previewCard = document.getElementById('compressimgPreviewCard'); |
| const originalImg = document.getElementById('compressimgPreviewOriginal'); |
| const processedImg = document.getElementById('compressimgPreviewProcessed'); |
|
|
| setButtonLoading(btn, true, 'Loading...'); |
|
|
| try { |
| const fd = new FormData(); |
| fd.append('file', compressImgFile); |
| fd.append('quality', document.getElementById('compressimg_quality').value); |
|
|
| const res = await fetch('/api/preview/compress-image', { method: 'POST', body: fd }); |
|
|
| if (!res.ok) { |
| const err = await res.json().catch(() => ({ detail: 'Failed' })); |
| throw new Error(err.detail); |
| } |
|
|
| const data = await res.json(); |
| originalImg.src = 'data:image/jpeg;base64,' + data.original; |
| processedImg.src = 'data:image/jpeg;base64,' + data.processed; |
| previewCard.style.display = 'block'; |
| previewCard.scrollIntoView({ behavior: 'smooth', block: 'start' }); |
| |
| const origKB = (data.original_size / 1024).toFixed(1); |
| const compKB = (data.compressed_size / 1024).toFixed(1); |
| showToast('Preview Ready', `${origKB} KB → ${compKB} KB`, 'success', 2000); |
|
|
| } catch (e) { |
| showToast('Error', e.message, 'error'); |
| } finally { |
| setButtonLoading(btn, false); |
| } |
| } |
|
|
| async function compressImage() { |
| if (!compressImgFile) { |
| showToast('No Image', 'Upload an image first', 'error'); |
| return; |
| } |
|
|
| const btn = document.getElementById('btnCompressImg'); |
| const progress = document.getElementById('compressimgProgress'); |
| const resultDiv = document.getElementById('compressimgResult'); |
| const statsDiv = document.getElementById('compressimgStats'); |
|
|
| setButtonLoading(btn, true, 'Compressing...'); |
| showProgress(progress, true, true); |
| resultDiv.style.display = 'none'; |
|
|
| try { |
| const fd = new FormData(); |
| fd.append('file', compressImgFile); |
| fd.append('quality', document.getElementById('compressimg_quality').value); |
| fd.append('output_name', document.getElementById('compressimg_output').value || 'compressed'); |
|
|
| const res = await fetch('/api/compress-image', { method: 'POST', body: fd }); |
|
|
| if (!res.ok) { |
| const err = await res.json().catch(() => ({ detail: 'Failed' })); |
| throw new Error(err.detail); |
| } |
|
|
| const blob = await res.blob(); |
| const contentDisposition = res.headers.get('content-disposition'); |
| let filename = 'compressed.jpg'; |
| if (contentDisposition) { |
| const match = contentDisposition.match(/filename="?([^"]+)"?/); |
| if (match) filename = match[1]; |
| } |
| downloadBlob(blob, filename); |
|
|
| |
| const originalSize = res.headers.get('X-Original-Size'); |
| const compressedSize = res.headers.get('X-Compressed-Size'); |
| const reduction = res.headers.get('X-Reduction-Percent'); |
|
|
| if (originalSize && compressedSize) { |
| const origKB = (parseInt(originalSize) / 1024).toFixed(1); |
| const compKB = (parseInt(compressedSize) / 1024).toFixed(1); |
| statsDiv.textContent = `${origKB} KB → ${compKB} KB (${reduction}% smaller)`; |
| resultDiv.style.display = 'block'; |
| } |
|
|
| showSuccessAnimation('Image Compressed!', `Reduced by ${reduction}%`); |
|
|
| } catch (e) { |
| showToast('Error', e.message, 'error'); |
| } finally { |
| setButtonLoading(btn, false); |
| showProgress(progress, false); |
| } |
| } |
|
|
|
|
| |
|
|
| function downloadBlob(blob, filename) { |
| const url = URL.createObjectURL(blob); |
| const a = document.createElement('a'); |
| a.href = url; |
| a.download = filename; |
| document.body.appendChild(a); |
| a.click(); |
| a.remove(); |
| URL.revokeObjectURL(url); |
| } |
|
|
|
|
| |
|
|
| let enhanceImgFile = null; |
|
|
| function initEnhanceImage() { |
| const dropZone = document.getElementById('enhanceImgDropZone'); |
| const fileInput = document.getElementById('enhanceimg_file'); |
| const btn = document.getElementById('btnEnhanceImg'); |
| const btnPreview = document.getElementById('btnEnhanceImgPreview'); |
|
|
| if (!dropZone || !fileInput) return; |
|
|
| initDropZone(dropZone, fileInput, { |
| maxSize: 50 * 1024 * 1024, |
| onFile: (file) => { |
| enhanceImgFile = file; |
| btn.disabled = !file; |
| btnPreview.disabled = !file; |
| document.getElementById('enhanceimgPreviewCard').style.display = 'none'; |
| if (file) showToast('Image Selected', file.name, 'success', 2000); |
| } |
| }); |
|
|
| btnPreview.addEventListener('click', () => previewEnhanceImage()); |
| btn.addEventListener('click', () => enhanceImage()); |
| } |
|
|
| async function previewEnhanceImage() { |
| if (!enhanceImgFile) { |
| showToast('No Image', 'Upload an image first', 'error'); |
| return; |
| } |
|
|
| const btn = document.getElementById('btnEnhanceImgPreview'); |
| const previewCard = document.getElementById('enhanceimgPreviewCard'); |
| const originalImg = document.getElementById('enhanceimgPreviewOriginal'); |
| const processedImg = document.getElementById('enhanceimgPreviewProcessed'); |
|
|
| setButtonLoading(btn, true, 'Loading...'); |
|
|
| try { |
| const fd = new FormData(); |
| fd.append('file', enhanceImgFile); |
| fd.append('level', document.getElementById('enhanceimg_level').value); |
| fd.append('upscale', document.getElementById('enhanceimg_upscale').value); |
|
|
| const res = await fetch('/api/preview/enhance-image', { method: 'POST', body: fd }); |
|
|
| if (!res.ok) { |
| const err = await res.json().catch(() => ({ detail: 'Failed' })); |
| throw new Error(err.detail); |
| } |
|
|
| const data = await res.json(); |
| originalImg.src = 'data:image/jpeg;base64,' + data.original; |
| processedImg.src = 'data:image/jpeg;base64,' + data.processed; |
| previewCard.style.display = 'block'; |
| previewCard.scrollIntoView({ behavior: 'smooth', block: 'start' }); |
| |
| showToast('Preview Ready', 'Compare original vs enhanced', 'success', 2000); |
|
|
| } catch (e) { |
| showToast('Error', e.message, 'error'); |
| } finally { |
| setButtonLoading(btn, false); |
| } |
| } |
|
|
| async function enhanceImage() { |
| if (!enhanceImgFile) { |
| showToast('No Image', 'Upload an image first', 'error'); |
| return; |
| } |
|
|
| const btn = document.getElementById('btnEnhanceImg'); |
| const progress = document.getElementById('enhanceimgProgress'); |
|
|
| setButtonLoading(btn, true, 'Enhancing...'); |
| showProgress(progress, true, true); |
|
|
| try { |
| const fd = new FormData(); |
| fd.append('file', enhanceImgFile); |
| fd.append('level', document.getElementById('enhanceimg_level').value); |
| fd.append('upscale', document.getElementById('enhanceimg_upscale').value); |
| fd.append('output_name', document.getElementById('enhanceimg_output').value || 'enhanced'); |
|
|
| const res = await fetch('/api/enhance-image', { method: 'POST', body: fd }); |
|
|
| if (!res.ok) { |
| const err = await res.json().catch(() => ({ detail: 'Failed' })); |
| throw new Error(err.detail); |
| } |
|
|
| const blob = await res.blob(); |
| const contentDisposition = res.headers.get('content-disposition'); |
| let filename = 'enhanced.jpg'; |
| if (contentDisposition) { |
| const match = contentDisposition.match(/filename="?([^"]+)"?/); |
| if (match) filename = match[1]; |
| } |
| downloadBlob(blob, filename); |
|
|
| showSuccessAnimation('Image Enhanced!', 'Download started'); |
|
|
| } catch (e) { |
| showToast('Error', e.message, 'error'); |
| } finally { |
| setButtonLoading(btn, false); |
| showProgress(progress, false); |
| } |
| } |
|
|
|
|
| |
|
|
| let enhancePdfFile = null; |
|
|
| function initEnhancePdf() { |
| const dropZone = document.getElementById('enhancePdfDropZone'); |
| const fileInput = document.getElementById('enhancepdf_file'); |
| const btn = document.getElementById('btnEnhancePdf'); |
|
|
| if (!dropZone || !fileInput) return; |
|
|
| initDropZone(dropZone, fileInput, { |
| maxSize: 100 * 1024 * 1024, |
| onFile: (file) => { |
| enhancePdfFile = file; |
| btn.disabled = !file; |
| document.getElementById('enhancepdfResult').style.display = 'none'; |
| if (file) showToast('PDF Selected', file.name, 'success', 2000); |
| } |
| }); |
|
|
| btn.addEventListener('click', () => enhancePdf()); |
| } |
|
|
| async function enhancePdf() { |
| if (!enhancePdfFile) { |
| showToast('No PDF', 'Upload a PDF first', 'error'); |
| return; |
| } |
|
|
| const btn = document.getElementById('btnEnhancePdf'); |
| const progress = document.getElementById('enhancepdfProgress'); |
| const resultDiv = document.getElementById('enhancepdfResult'); |
| const statsDiv = document.getElementById('enhancepdfStats'); |
|
|
| setButtonLoading(btn, true, 'Enhancing...'); |
| showProgress(progress, true, true); |
| resultDiv.style.display = 'none'; |
|
|
| try { |
| const fd = new FormData(); |
| fd.append('file', enhancePdfFile); |
| fd.append('level', document.getElementById('enhancepdf_level').value); |
| fd.append('output_name', document.getElementById('enhancepdf_output').value || 'enhanced.pdf'); |
|
|
| const res = await fetch('/api/enhance-pdf', { method: 'POST', body: fd }); |
|
|
| if (!res.ok) { |
| const err = await res.json().catch(() => ({ detail: 'Failed' })); |
| throw new Error(err.detail); |
| } |
|
|
| const blob = await res.blob(); |
| const filename = document.getElementById('enhancepdf_output').value || 'enhanced.pdf'; |
| downloadBlob(blob, filename.endsWith('.pdf') ? filename : filename + '.pdf'); |
|
|
| |
| const originalSize = res.headers.get('X-Original-Size'); |
| const enhancedSize = res.headers.get('X-Enhanced-Size'); |
| const imagesEnhanced = res.headers.get('X-Images-Enhanced'); |
|
|
| if (originalSize && enhancedSize) { |
| const origMB = (parseInt(originalSize) / 1024 / 1024).toFixed(2); |
| const enhMB = (parseInt(enhancedSize) / 1024 / 1024).toFixed(2); |
| const change = (((parseInt(enhancedSize) - parseInt(originalSize)) / parseInt(originalSize)) * 100).toFixed(1); |
| statsDiv.textContent = `${origMB} MB → ${enhMB} MB (${change > 0 ? '+' : ''}${change}%), ${imagesEnhanced} images enhanced`; |
| resultDiv.style.display = 'block'; |
| } |
|
|
| showSuccessAnimation('PDF Enhanced!', `${imagesEnhanced} images improved`); |
|
|
| } catch (e) { |
| showToast('Error', e.message, 'error'); |
| } finally { |
| setButtonLoading(btn, false); |
| showProgress(progress, false); |
| } |
| } |
|
|
|
|
| |
|
|
| function initImageTools() { |
| initRemoveBackground(); |
| initImageWatermark(); |
| initResizeImage(); |
| initConvertImage(); |
| initCompressImage(); |
| initEnhanceImage(); |
| initEnhancePdf(); |
| } |
|
|