Spaces:
Sleeping
Sleeping
| 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 = '<i class="fas fa-spinner fa-spin"></i> 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 = '<i class="fas fa-sync-alt"></i> 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(); | |
| }); |