diff --git a/static/index.html b/static/index.html index ae35211..5bf52bf 100644 --- a/static/index.html +++ b/static/index.html @@ -983,14 +983,14 @@
📸 Photo (optional)
-
+
📸
Tap to take photo
- +
@@ -2466,62 +2466,47 @@ // ═══════════════════════════════════════════════════════════════════════ // MANUAL MODE — Photo // ═══════════════════════════════════════════════════════════════════════ - let manualPhotoStream = null; + let manualPhotoFile = null; + let manualPhotoBlob = null; + let manualPhotoGps = null; - async function startManualPhoto() { - if (manualPhotoStream) return; - try { - manualPhotoStream = await navigator.mediaDevices.getUserMedia({ - video: { facingMode: { ideal: 'environment' }, width: { ideal: 1920 }, height: { ideal: 1080 } }, - audio: false, - }); - const video = document.getElementById('manPhotoVideo'); - if (!video) return; - video.srcObject = manualPhotoStream; - await video.play(); - document.getElementById('manPhotoPlaceholder').style.display = 'none'; - video.style.display = 'block'; - document.getElementById('manPhotoCaptureRow').style.display = 'flex'; - } catch (e) { - showToast('Camera failed: ' + e.message, true); - } - } + async function handleManualPhotoFile(event) { + const file = event.target.files?.[0]; + if (!file) return; + manualPhotoFile = file; + manualPhotoBlob = file; // Use original file (preserves EXIF) - async function captureManualPhoto() { - const video = document.getElementById('manPhotoVideo'); - if (!video || !manualPhotoStream) return; - - const canvas = document.createElement('canvas'); - canvas.width = video.videoWidth; - canvas.height = video.videoHeight; - canvas.getContext('2d').drawImage(video, 0, 0); - manualPhotoBlob = await new Promise(r => canvas.toBlob(r, 'image/jpeg', 0.85)); - - const url = URL.createObjectURL(manualPhotoBlob); + // Show preview + const url = URL.createObjectURL(file); document.getElementById('manPhotoPreview').src = url; document.getElementById('manPhotoPreview').style.display = 'block'; document.getElementById('manPhotoRetake').style.display = 'block'; - stopManualPhoto(); + document.getElementById('manPhotoPlaceholder').style.display = 'none'; + + // Try EXIF GPS from the original file + manualPhotoGps = await extractGpsFromPhoto(file); + if (manualPhotoGps) { + const coords = manualPhotoGps.lat.toFixed(6) + ', ' + manualPhotoGps.lng.toFixed(6); + const parkingEl = document.getElementById('manParkingLocation'); + if (parkingEl && !parkingEl.value.trim()) { + parkingEl.value = coords; + } + const mapLinkEl = document.getElementById('manMapLink'); + if (mapLinkEl && !mapLinkEl.value.trim()) { + mapLinkEl.value = `https://www.openstreetmap.org/?mlat=${manualPhotoGps.lat}&mlon=${manualPhotoGps.lng}&zoom=17`; + } + showToast('📍 GPS extracted from photo!'); + } } function retakeManualPhoto() { + manualPhotoFile = null; manualPhotoBlob = null; + manualPhotoGps = null; document.getElementById('manPhotoPreview').style.display = 'none'; document.getElementById('manPhotoRetake').style.display = 'none'; - startManualPhoto(); - } - - function stopManualPhoto() { - if (manualPhotoStream) { - manualPhotoStream.getTracks().forEach(t => t.stop()); - manualPhotoStream = null; - } - const video = document.getElementById('manPhotoVideo'); - if (video) video.style.display = 'none'; - const ph = document.getElementById('manPhotoPlaceholder'); - if (ph) ph.style.display = ''; - const row = document.getElementById('manPhotoCaptureRow'); - if (row) row.style.display = 'none'; + document.getElementById('manPhotoPlaceholder').style.display = ''; + document.getElementById('manPhotoInput').value = ''; } // ═══════════════════════════════════════════════════════════════════════ @@ -2536,12 +2521,16 @@ } let photoPath = null; - if (manualPhotoBlob) { + if (manualPhotoFile) { try { const fd = new FormData(); - fd.append('file', manualPhotoBlob, 'photo.jpg'); + fd.append('file', manualPhotoFile, manualPhotoFile.name || 'photo.jpg'); const upData = await api('/api/upload/photo', { method: 'POST', body: fd }); photoPath = upData.path; + // If server returned EXIF GPS and we don't have GPS from the file yet, use it + if (upData.exif_gps && !manualPhotoGps) { + manualPhotoGps = upData.exif_gps; + } } catch (e) { /* photo upload failed, continue without */ } } @@ -3780,55 +3769,18 @@ } // ═══════════════════════════════════════════════════════════════════════ - // HOOK: EXIF GPS extraction after camera capture in manual mode + // Extract GPS from a photo file using exifr library // ═══════════════════════════════════════════════════════════════════════ - // Patch captureManualPhoto to also try EXIF GPS extraction - const _origCaptureManualPhoto = captureManualPhoto; - captureManualPhoto = async function() { - await _origCaptureManualPhoto(); - // After capture, try EXIF GPS from the blob - if (manualPhotoBlob) { - try { - const gps = await extractGpsFromPhoto(manualPhotoBlob); - if (gps) { - const coords = gps.lat.toFixed(6) + ', ' + gps.lng.toFixed(6); - const parkingEl = document.getElementById('manParkingLocation'); - if (parkingEl && !parkingEl.value.trim()) { - parkingEl.value = coords; - } - const mapLinkEl = document.getElementById('manMapLink'); - if (mapLinkEl && !mapLinkEl.value.trim()) { - mapLinkEl.value = `https://www.openstreetmap.org/?mlat=${gps.lat}&mlon=${gps.lng}&zoom=17`; - } - // Show GPS badge near photo - const photoSection = document.querySelector('#addManualMode .form-section'); - let gpsTag = document.getElementById('manPhotoGpsTag'); - if (!gpsTag) { - gpsTag = document.createElement('span'); - gpsTag.id = 'manPhotoGpsTag'; - gpsTag.className = 'gps-badge ok'; - gpsTag.style.marginLeft = '8px'; - const title = document.querySelector('#addManualMode .form-section-title'); - if (title) title.appendChild(gpsTag); - } - gpsTag.textContent = '📍 GPS from photo'; - gpsTag.style.display = 'inline-flex'; - showToast('📍 GPS extracted from photo!'); - } - } catch (e) { - // GPS extraction failure is non-critical - console.warn('EXIF GPS from capture failed:', e.message); - } + async function extractGpsFromPhoto(file) { + if (!file) return null; + try { + const gps = await exifr.gps(file); + return gps || null; + } catch (e) { + console.warn('EXIF GPS extraction failed:', e.message); + return null; } - }; - - // Patch retakeManualPhoto to clear GPS tag - const _origRetakeManualPhoto = retakeManualPhoto; - retakeManualPhoto = function() { - _origRetakeManualPhoto(); - const gpsTag = document.getElementById('manPhotoGpsTag'); - if (gpsTag) gpsTag.style.display = 'none'; - }; + } // Boot auth at page load document.addEventListener('DOMContentLoaded', () => { initAuth(); });