class CameraController { constructor() { this.captureInterval = null; this.countdown = 5; this.photoCount = 0; this.isCapturing = false; this.initializeElements(); this.bindEvents(); this.loadPhotoCount(); } initializeElements() { this.startBtn = document.getElementById('startBtn'); this.stopBtn = document.getElementById('stopBtn'); this.testBtn = document.getElementById('testBtn'); this.statusDot = document.getElementById('statusDot'); this.statusText = document.getElementById('statusText'); this.countdownElement = document.getElementById('countdown'); this.photoCountElement = document.getElementById('photoCount'); } bindEvents() { this.startBtn.addEventListener('click', () => this.startCapture()); this.stopBtn.addEventListener('click', () => this.stopCapture()); this.testBtn.addEventListener('click', () => this.testConnection()); // Start capture automatically when page loads window.addEventListener('load', () => { setTimeout(() => this.startCapture(), 1000); }); // Stop capture when page is about to unload window.addEventListener('beforeunload', (e) => { if (this.isCapturing) { this.stopCaptureSync(); } }); } async startCapture() { if (this.isCapturing) return; try { const response = await fetch('/api/start_capture', { method: 'POST', headers: { 'Content-Type': 'application/json' } }); const data = await response.json(); if (data.status === 'success') { this.isCapturing = true; this.updateStatus(true); this.showNotification('Capture started successfully!', 'success'); this.startCountdown(); } else { this.showNotification(data.message, 'info'); } } catch (error) { console.error('Error starting capture:', error); this.showNotification('Failed to start capture. Please check connection.', 'error'); } } async stopCapture() { if (!this.isCapturing) return; try { const response = await fetch('/api/stop_capture', { method: 'POST', headers: { 'Content-Type': 'application/json' } }); const data = await response.json(); if (data.status === 'success') { this.isCapturing = false; this.updateStatus(false); this.stopCountdown(); this.showNotification('Capture stopped successfully!', 'success'); } } catch (error) { console.error('Error stopping capture:', error); this.showNotification('Failed to stop capture.', 'error'); } } stopCaptureSync() { // This is a synchronous version for beforeunload fetch('/api/stop_capture', { method: 'POST', headers: { 'Content-Type': 'application/json' }, keepalive: true // Important for beforeunload requests }).catch(console.error); } startCountdown() { this.countdown = 5; this.updateCountdown(); this.captureInterval = setInterval(() => { this.countdown--; this.updateCountdown(); if (this.countdown <= 0) { this.simulateCapture(); this.countdown = 5; } }, 1000); } stopCountdown() { if (this.captureInterval) { clearInterval(this.captureInterval); this.captureInterval = null; } this.countdown = 5; this.updateCountdown(); } updateCountdown() { if (this.countdownElement) { this.countdownElement.textContent = this.countdown; this.countdownElement.style.color = this.countdown <= 3 ? '#ef4444' : '#4f46e5'; this.countdownElement.style.fontWeight = this.countdown <= 3 ? 'bold' : 'normal'; } } updateStatus(isActive) { if (this.statusDot) { if (isActive) { this.statusDot.classList.add('active'); this.statusText.textContent = 'Camera is active'; this.statusText.style.color = '#28a745'; } else { this.statusDot.classList.remove('active'); this.statusText.textContent = 'Camera is inactive'; this.statusText.style.color = '#dc3545'; } } } simulateCapture() { // Simulate photo capture animation const cameraIcon = document.querySelector('.camera-icon i'); if (cameraIcon) { cameraIcon.style.transform = 'scale(1.2)'; cameraIcon.style.color = '#10b981'; setTimeout(() => { cameraIcon.style.transform = 'scale(1)'; cameraIcon.style.color = '#555'; }, 300); } // Update photo count this.photoCount++; this.updatePhotoCount(); // Show capture animation this.showCaptureAnimation(); } showCaptureAnimation() { const animation = document.createElement('div'); animation.style.cssText = ` position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); font-size: 4rem; color: white; background: rgba(0,0,0,0.7); padding: 20px; border-radius: 50%; z-index: 1000; opacity: 0; animation: flash 1s ease-out; `; const style = document.createElement('style'); style.textContent = ` @keyframes flash { 0% { opacity: 0; transform: translate(-50%, -50%) scale(0.5); } 50% { opacity: 1; transform: translate(-50%, -50%) scale(1.2); } 100% { opacity: 0; transform: translate(-50%, -50%) scale(1.5); } } `; document.head.appendChild(style); animation.innerHTML = '📸'; document.body.appendChild(animation); setTimeout(() => { document.body.removeChild(animation); document.head.removeChild(style); }, 1000); } async loadPhotoCount() { try { const response = await fetch('/api/get_images'); const data = await response.json(); if (data.status === 'success') { this.photoCount = data.images.length; this.updatePhotoCount(); } } catch (error) { console.error('Error loading photo count:', error); } } updatePhotoCount() { if (this.photoCountElement) { this.photoCountElement.textContent = this.photoCount; // Add animation this.photoCountElement.style.transform = 'scale(1.2)'; setTimeout(() => { this.photoCountElement.style.transform = 'scale(1)'; }, 300); } } async testConnection() { try { this.testBtn.innerHTML = ' Testing...'; const response = await fetch('/api/get_images'); const data = await response.json(); if (data.status === 'success') { this.showNotification('Connection successful! Server is responding.', 'success'); } else { this.showNotification('Server responded with an error.', 'warning'); } } catch (error) { this.showNotification('Connection failed. Please check server.', 'error'); } finally { this.testBtn.innerHTML = ' Test Connection'; } } showNotification(message, type = 'info') { // Remove existing notifications const existing = document.querySelector('.notification'); if (existing) existing.remove(); const notification = document.createElement('div'); notification.className = `notification ${type}`; notification.style.cssText = ` position: fixed; top: 20px; right: 20px; padding: 1rem 1.5rem; border-radius: 10px; color: white; font-weight: 500; z-index: 1000; animation: slideIn 0.3s ease; box-shadow: 0 4px 12px rgba(0,0,0,0.15); `; const colors = { success: '#10b981', error: '#ef4444', warning: '#f59e0b', info: '#0ea5e9' }; notification.style.background = colors[type] || colors.info; notification.textContent = message; document.body.appendChild(notification); setTimeout(() => { notification.style.animation = 'slideOut 0.3s ease'; setTimeout(() => notification.remove(), 300); }, 3000); // Add animations const style = document.createElement('style'); style.textContent = ` @keyframes slideIn { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } } @keyframes slideOut { from { transform: translateX(0); opacity: 1; } to { transform: translateX(100%); opacity: 0; } } `; document.head.appendChild(style); setTimeout(() => document.head.removeChild(style), 3300); } } // Initialize the camera controller when page loads document.addEventListener('DOMContentLoaded', () => { new CameraController(); });