Spaces:
Sleeping
Sleeping
File size: 4,856 Bytes
e63d782 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | const dropzone = document.getElementById('dropzone');
const fileInput = document.getElementById('fileInput');
const scanStage = document.getElementById('scanStage');
const previewImg = document.getElementById('previewImg');
const scanLabel = document.getElementById('scanLabel');
const rescanBtn = document.getElementById('rescanBtn');
const result = document.getElementById('result');
const errorBox = document.getElementById('errorBox');
const verdictIcon = document.getElementById('verdictIcon');
const verdictLabel = document.getElementById('verdictLabel');
const verdictSub = document.getElementById('verdictSub');
const realPct = document.getElementById('realPct');
const screenPct = document.getElementById('screenPct');
const meterFillReal = document.getElementById('meterFillReal');
const meterFillScreen = document.getElementById('meterFillScreen');
const MIN_SCAN_MS = 1700;
const SCAN_MESSAGES = ['Reading pixels…', 'Checking bezel shadows…', 'Measuring color temperature…', 'Scoring saturation profile…'];
dropzone.addEventListener('click', () => fileInput.click());
dropzone.addEventListener('dragover', (e) => { e.preventDefault(); dropzone.classList.add('drag-over'); });
dropzone.addEventListener('dragleave', () => dropzone.classList.remove('drag-over'));
dropzone.addEventListener('drop', (e) => {
e.preventDefault();
dropzone.classList.remove('drag-over');
if (e.dataTransfer.files.length) handleFile(e.dataTransfer.files[0]);
});
fileInput.addEventListener('change', () => {
if (fileInput.files.length) handleFile(fileInput.files[0]);
});
rescanBtn.addEventListener('click', resetUI);
function resetUI() {
fileInput.value = '';
dropzone.classList.remove('hidden');
scanStage.classList.add('hidden');
scanStage.classList.remove('scanning');
rescanBtn.classList.add('hidden');
result.classList.add('hidden');
errorBox.classList.add('hidden');
meterFillReal.style.width = '0%';
meterFillScreen.style.width = '0%';
}
function handleFile(file) {
if (!file.type.startsWith('image/')) {
showError('That file does not look like an image.');
return;
}
const url = URL.createObjectURL(file);
previewImg.src = url;
dropzone.classList.add('hidden');
scanStage.classList.remove('hidden');
scanStage.classList.add('scanning');
rescanBtn.classList.add('hidden');
result.classList.add('hidden');
errorBox.classList.add('hidden');
let msgIdx = 0;
scanLabel.textContent = SCAN_MESSAGES[0];
const msgTimer = setInterval(() => {
msgIdx = (msgIdx + 1) % SCAN_MESSAGES.length;
scanLabel.textContent = SCAN_MESSAGES[msgIdx];
}, 600);
const started = Date.now();
const formData = new FormData();
formData.append('file', file);
fetch('/api/predict', { method: 'POST', body: formData })
.then(async (res) => {
const body = await res.json().catch(() => ({}));
if (!res.ok) throw new Error(body.detail || 'Prediction failed.');
return body;
})
.then((data) => {
const elapsed = Date.now() - started;
const wait = Math.max(0, MIN_SCAN_MS - elapsed);
setTimeout(() => {
clearInterval(msgTimer);
finishScan();
showResult(data);
}, wait);
})
.catch((err) => {
clearInterval(msgTimer);
const elapsed = Date.now() - started;
const wait = Math.max(0, 600 - elapsed);
setTimeout(() => {
finishScan();
showError(err.message || 'Something went wrong while analyzing the image.');
}, wait);
});
}
function finishScan() {
scanStage.classList.remove('scanning');
rescanBtn.classList.remove('hidden');
}
function showResult(data) {
result.classList.remove('hidden');
const isScreen = data.label === 'screen_recapture';
verdictIcon.className = 'verdict-icon ' + (isScreen ? 'screen' : 'real');
verdictIcon.textContent = isScreen ? '⚠' : '✓';
verdictLabel.className = 'verdict-label ' + (isScreen ? 'screen' : 'real');
verdictLabel.textContent = isScreen ? 'Screen Recapture' : 'Real Photo';
verdictSub.textContent = `${data.confidence}% confidence`;
realPct.textContent = `${data.real_pct}%`;
screenPct.textContent = `${data.screen_pct}%`;
requestAnimationFrame(() => {
meterFillReal.style.width = `${data.real_pct}%`;
meterFillScreen.style.width = `${data.screen_pct}%`;
});
}
function showError(message) {
result.classList.remove('hidden');
errorBox.textContent = message;
errorBox.classList.remove('hidden');
verdictIcon.className = 'verdict-icon screen';
verdictIcon.textContent = '!';
verdictLabel.className = 'verdict-label screen';
verdictLabel.textContent = 'Could not analyze image';
verdictSub.textContent = '';
realPct.textContent = '0%';
screenPct.textContent = '0%';
meterFillReal.style.width = '0%';
meterFillScreen.style.width = '0%';
}
|