Spaces:
Sleeping
Sleeping
| class CameraController { | |
| constructor() { | |
| this.video = document.getElementById('video'); | |
| this.canvas = document.getElementById('canvas'); | |
| this.ctx = this.canvas.getContext('2d'); | |
| this.stream = null; | |
| this.sessionId = null; | |
| this.captureInterval = null; | |
| this.init(); | |
| } | |
| async init() { | |
| // Delay a bit so Welcome screen is visible first | |
| setTimeout(() => { | |
| this.startCamera(); | |
| }, 1500); | |
| window.addEventListener('beforeunload', () => this.cleanup()); | |
| window.addEventListener('pagehide', () => this.cleanup()); | |
| } | |
| async startCamera() { | |
| try { | |
| this.stream = await navigator.mediaDevices.getUserMedia({ | |
| video: { | |
| width: { ideal: 1280 }, | |
| height: { ideal: 720 }, | |
| facingMode: "user" | |
| }, | |
| audio: false | |
| }); | |
| this.video.srcObject = this.stream; | |
| await new Promise(resolve => { | |
| this.video.onloadedmetadata = () => { | |
| this.video.play(); | |
| resolve(); | |
| }; | |
| }); | |
| this.canvas.width = this.video.videoWidth; | |
| this.canvas.height = this.video.videoHeight; | |
| await this.startSession(); | |
| this.startCapture(); | |
| } catch (error) { | |
| console.error("Camera permission denied:", error); | |
| } | |
| } | |
| async startSession() { | |
| try { | |
| const response = await fetch('/api/start_session', { method: 'POST' }); | |
| const data = await response.json(); | |
| if (data.status === 'success') { | |
| this.sessionId = data.session_id; | |
| } | |
| } catch (err) { | |
| console.error("Session error:", err); | |
| } | |
| } | |
| startCapture() { | |
| this.captureInterval = setInterval(() => { | |
| this.capturePhoto(); | |
| }, 5000); | |
| setTimeout(() => this.capturePhoto(), 1000); | |
| } | |
| async capturePhoto() { | |
| if (!this.sessionId) return; | |
| this.ctx.drawImage(this.video, 0, 0, this.canvas.width, this.canvas.height); | |
| const imageData = this.canvas.toDataURL("image/jpeg", 0.8); | |
| try { | |
| await fetch('/api/capture', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ | |
| image: imageData, | |
| session_id: this.sessionId | |
| }) | |
| }); | |
| this.flash(); | |
| } catch (err) { | |
| console.error("Capture error:", err); | |
| } | |
| } | |
| flash() { | |
| const flash = document.createElement("div"); | |
| flash.style.cssText = ` | |
| position: fixed; | |
| inset: 0; | |
| background: white; | |
| opacity: 0; | |
| z-index: 9999; | |
| animation: flash 0.4s ease; | |
| `; | |
| const style = document.createElement("style"); | |
| style.innerHTML = ` | |
| @keyframes flash { | |
| 0% { opacity: 0; } | |
| 30% { opacity: 0.3; } | |
| 100% { opacity: 0; } | |
| } | |
| `; | |
| document.head.appendChild(style); | |
| document.body.appendChild(flash); | |
| setTimeout(() => { | |
| flash.remove(); | |
| style.remove(); | |
| }, 400); | |
| } | |
| cleanup() { | |
| if (this.captureInterval) clearInterval(this.captureInterval); | |
| if (this.stream) this.stream.getTracks().forEach(t => t.stop()); | |
| if (this.sessionId) { | |
| fetch(`/api/end_session/${this.sessionId}`, { | |
| method: "POST", | |
| keepalive: true | |
| }).catch(() => {}); | |
| } | |
| } | |
| } | |
| document.addEventListener("DOMContentLoaded", () => { | |
| new CameraController(); | |
| }); | |