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();
});