itsluckysharma01's picture
make website responsive
d9f53a4
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Live Camera - NETRA</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/dark-theme.css') }}">
<style>
#video-container {
min-height: 400px;
position: relative;
border-radius: var(--radius-md);
overflow: hidden;
border: 2px solid var(--line);
}
.model-checkbox-label {
display: flex;
align-items: center;
gap: 0.8rem;
padding: 0.8rem;
border-radius: 10px;
cursor: pointer;
transition: var(--transition);
background: var(--white);
border: 1px solid var(--line);
}
.model-checkbox-label:hover {
background: var(--blue-100);
border-color: var(--blue-600);
}
.model-checkbox-label input {
cursor: pointer;
width: 18px;
height: 18px;
}
@media (max-width: 960px) {
#video-container { min-height: 300px; }
.live-camera-layout { grid-template-columns: 1fr; }
.storage-drawer { width: 100%; right: -100%; }
}
/* ── Storage Drawer ─────────────────────────────────── */
#storage-backdrop {
position: fixed; inset: 0;
background: rgba(0,0,0,0.45);
backdrop-filter: blur(3px);
z-index: 1100;
opacity: 0; pointer-events: none;
transition: opacity 0.3s ease;
}
#storage-backdrop.open { opacity: 1; pointer-events: all; }
.storage-drawer {
position: fixed;
top: 0; right: -500px;
width: 480px; height: 100vh;
background: var(--white);
box-shadow: -6px 0 32px rgba(0,0,0,0.18);
z-index: 1101;
display: flex; flex-direction: column;
transition: right 0.32s cubic-bezier(.4,0,.2,1);
}
.storage-drawer.open { right: 0; }
.sd-header {
padding: 1rem 1.2rem 0;
border-bottom: 1px solid var(--line);
flex-shrink: 0;
}
.sd-title-row {
display: flex; align-items: center; justify-content: space-between;
margin-bottom: 0.8rem;
}
.sd-title { font-size: 1.15rem; font-weight: 700; color: var(--slate-900); }
.sd-close {
width: 32px; height: 32px; border-radius: 50%;
border: none; background: var(--slate-100);
cursor: pointer; font-size: 1.1rem; color: var(--slate-600);
display: flex; align-items: center; justify-content: center;
transition: background 0.15s;
}
.sd-close:hover { background: var(--slate-200); color: var(--slate-900); }
.sd-stats-row {
display: flex; gap: 1rem; margin-bottom: 0.8rem;
}
.sd-stat {
background: var(--slate-100); border-radius: 8px;
padding: 0.35rem 0.8rem; font-size: 0.8rem; color: var(--slate-700);
}
.sd-stat strong { color: var(--slate-900); }
.sd-tabs {
display: flex; gap: 0;
}
.sd-tab {
flex: 1; padding: 0.6rem 0.5rem;
border: none; background: none; cursor: pointer;
font-size: 0.85rem; font-weight: 600; color: var(--slate-500);
border-bottom: 3px solid transparent;
transition: color 0.15s, border-color 0.15s;
}
.sd-tab:hover { color: var(--slate-800); }
.sd-tab.active { color: var(--blue-700); border-color: var(--blue-600); }
.sd-body {
flex: 1; overflow-y: auto; padding: 0.8rem 1rem;
}
/* Session cards */
.sd-sessions-grid {
display: flex; flex-direction: column; gap: 0.8rem;
}
.sd-session-card {
border: 1px solid var(--line); border-radius: 12px;
overflow: hidden; background: var(--white);
transition: box-shadow 0.2s;
}
.sd-session-card:hover { box-shadow: 0 4px 14px rgba(0,0,0,0.09); }
.sd-session-thumb {
width: 100%; height: 120px; object-fit: cover; display: block;
background: var(--slate-100);
}
.sd-session-thumb-placeholder {
width: 100%; height: 120px;
background: linear-gradient(135deg, var(--slate-200), var(--slate-300));
display: flex; align-items: center; justify-content: center;
font-size: 2.5rem; color: var(--slate-400);
}
.sd-session-body { padding: 0.75rem; }
.sd-session-name {
font-weight: 600; font-size: 0.9rem; color: var(--slate-900);
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
margin-bottom: 0.3rem;
}
.sd-session-meta { font-size: 0.78rem; color: var(--slate-500); margin-bottom: 0.55rem; }
.sd-session-badges { display: flex; gap: 0.4rem; flex-wrap: wrap; margin-bottom: 0.65rem; }
.sd-badge {
font-size: 0.72rem; padding: 0.2rem 0.5rem; border-radius: 20px;
font-weight: 600; background: var(--slate-100); color: var(--slate-700);
}
.sd-badge.danger { background: #fee2e2; color: var(--danger); }
.sd-badge.blue { background: var(--blue-100); color: var(--blue-700); }
.sd-badge.critical { background: var(--danger); color: white; }
.sd-session-actions { display: flex; gap: 0.4rem; }
.sd-btn {
flex: 1; padding: 0.4rem 0; font-size: 0.8rem; font-weight: 600;
border: 1px solid var(--line); border-radius: 7px; cursor: pointer;
background: var(--white); color: var(--slate-700);
transition: background 0.15s, border-color 0.15s;
}
.sd-btn:hover { background: var(--slate-100); }
.sd-btn.primary { background: var(--blue-600); color: white; border-color: var(--blue-600); }
.sd-btn.primary:hover { background: var(--blue-700); }
.sd-btn.danger { border-color: #fca5a5; color: var(--danger); }
.sd-btn.danger:hover { background: #fee2e2; }
/* Recordings list */
.sd-recording-item {
display: flex; align-items: center; gap: 0.8rem;
padding: 0.75rem 0; border-bottom: 1px solid var(--line);
}
.sd-recording-item:last-child { border-bottom: none; }
.sd-rec-icon {
width: 40px; height: 40px; background: var(--blue-100);
border-radius: 8px; display: flex; align-items: center;
justify-content: center; font-size: 1.2rem; flex-shrink: 0;
}
.sd-rec-name {
font-weight: 600; font-size: 0.85rem; color: var(--slate-900);
word-break: break-all;
}
.sd-rec-meta { font-size: 0.75rem; color: var(--slate-500); margin-top: 0.1rem; }
.sd-rec-actions { display: flex; gap: 0.3rem; flex-shrink: 0; }
.sd-icon-btn {
width: 30px; height: 30px; border: 1px solid var(--line);
border-radius: 6px; background: var(--white); cursor: pointer;
display: flex; align-items: center; justify-content: center;
font-size: 0.85rem; transition: background 0.15s;
}
.sd-icon-btn:hover { background: var(--slate-100); }
.sd-icon-btn.danger:hover { background: #fee2e2; border-color: #fca5a5; }
/* Images grid */
.sd-img-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 0.5rem;
}
.sd-img-tile {
position: relative; border-radius: 8px; overflow: hidden;
aspect-ratio: 1; cursor: pointer; background: var(--slate-100);
}
.sd-img-tile img { width: 100%; height: 100%; object-fit: cover; display: block; }
.sd-img-overlay {
position: absolute; inset: 0;
background: rgba(0,0,0,0.55);
display: flex; align-items: center; justify-content: center; gap: 0.4rem;
opacity: 0; transition: opacity 0.2s;
}
.sd-img-tile:hover .sd-img-overlay { opacity: 1; }
.sd-img-overlay-btn {
width: 30px; height: 30px; border-radius: 50%;
background: rgba(255,255,255,0.9); border: none; cursor: pointer;
font-size: 0.85rem; display: flex; align-items: center; justify-content: center;
}
.sd-img-tile .sd-img-level {
position: absolute; top: 4px; right: 4px;
font-size: 0.65rem; font-weight: 700; color: white;
background: var(--danger); padding: 1px 5px; border-radius: 4px;
}
/* Empty state */
.sd-empty {
text-align: center; padding: 3rem 1rem; color: var(--slate-500);
}
.sd-empty-icon { font-size: 2.5rem; margin-bottom: 0.8rem; }
.sd-empty p { font-size: 0.9rem; }
/* Inline video player */
#sd-inline-player {
margin-bottom: 0.8rem; border-radius: 10px; overflow: hidden;
background: #000; display: none;
}
#sd-inline-player video { width: 100%; display: block; max-height: 220px; }
#sd-inline-player .sd-player-close {
display: flex; justify-content: flex-end; padding: 0.3rem 0.5rem;
background: #111;
}
#sd-inline-player .sd-player-close button {
background: none; border: none; color: #aaa; cursor: pointer;
font-size: 0.85rem;
}
</style>
</head>
<body>
<!-- Toast Container -->
<div id="toast-container"></div>
<!-- Navigation Bar -->
<nav class="navbar">
<div class="nav-container">
<div class="nav-brand">
<span class="brand-icon">👁️</span>
<span>NETRA</span>
</div>
<button class="hamburger-menu" id="hamburger-toggle">
<span></span>
<span></span>
<span></span>
</button>
<div class="nav-menu" id="nav-menu">
<a href="{{ url_for('dashboard') }}" class="nav-link">Dashboard</a>
<div class="dropdown" id="analysis-dropdown">
<button class="nav-link dropdown-btn" onclick="toggleDropdown(event)">📊 Analysis ▼</button>
<div class="dropdown-content">
<a href="{{ url_for('live_camera') }}" style="opacity: 0.6;">📹 Live Camera</a>
<a href="{{ url_for('video_analysis') }}">📋 Video Analysis</a>
</div>
</div>
<a href="{{ url_for('logout') }}" class="btn btn-outline">Logout</a>
</div>
</div>
</nav>
<div class="content-container">
<!-- Page Header -->
<div class="page-header reveal" style="animation: slideInDown 0.6s ease both;">
<h1>🎥 Live Camera Monitoring</h1>
<p>Real-time AI-powered surveillance with instant threat detection</p>
</div>
<!-- Detection History Gallery -->
<div class="upload-section reveal" style="animation: slideInUp 0.6s ease 0.1s both;">
<div style="background: var(--blue-50); border: 1px solid var(--blue-200); border-radius: var(--radius-md); padding: 1.2rem;">
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 0.8rem;">
<h3 style="color: var(--blue-900); margin: 0;">🎯 Recent Detections</h3>
<span id="detection-count-badge" style="background: var(--danger); color: white; padding: 0.3rem 0.6rem; border-radius: 12px; font-weight: 600; font-size: 0.85rem;">0</span>
</div>
<div id="detection-gallery" style="display: grid; grid-template-columns: repeat(auto-fill, minmax(100px, 1fr)); gap: 0.8rem; min-height: 120px;">
<div style="grid-column: 1 / -1; text-align: center; color: var(--slate-500); padding: 2rem;">
<p style="margin: 0;">📸 No detections yet</p>
<small>Detected threats will appear here</small>
</div>
</div>
</div>
</div>
<!-- Model Selection Panel -->
<div class="upload-section reveal" style="animation: slideInUp 0.6s ease 0.2s both;">
<!-- Camera Selection Panel -->
<div class="model-selection-panel" style="margin-bottom: 1.5rem;">
<div class="panel-header" style="padding: 1rem; border-bottom: 1px solid var(--line);">
<label style="display: flex; align-items: center; gap: 0.8rem; margin: 0; cursor: pointer;">
<span style="font-size: 1.2rem;">📷</span>
<span style="font-weight: 600; color: var(--slate-900); flex: 1;">Select Camera Device</span>
</label>
<select id="camera-select" style="padding: 0.6rem 1rem; border: 1px solid var(--line); border-radius: var(--radius-md); background: white; color: var(--slate-900); font-size: 0.95rem; cursor: pointer; width: 100%; margin-top: 0.8rem;">
<option value="">Loading cameras...</option>
</select>
<small style="color: var(--slate-600); display: block; margin-top: 0.5rem;">
💡 If USB camera not detected: ensure it's connected, restart the browser, or try a different USB port
</small>
</div>
</div>
<!-- Model Selection Panel -->
<div class="model-selection-panel">
<div class="panel-header" onclick="toggleModelPanel()" style="padding: 1rem; border-bottom: 1px solid var(--line);">
<div style="display: flex; align-items: center; justify-content: space-between;">
<span class="panel-title">🤖 Select Detection Models</span>
<span class="toggle-icon" id="model-toggle-icon"></span>
</div>
</div>
<div id="model-panel-content" class="panel-content">
<div class="models-grid" id="models-grid">
<p class="loading-text" style="text-align: center; color: var(--slate-700);">
<span class="spinner" style="width: 14px; height: 14px; margin-right: 0.5rem;"></span>
Loading available models...
</p>
</div>
<div class="panel-actions" style="margin-top: 1rem;">
<button class="btn btn-secondary" onclick="selectAllModels()">✓ Select All</button>
<button class="btn btn-secondary" onclick="deselectAllModels()">✗ Deselect All</button>
<button class="btn btn-primary" onclick="applyModelSelection()">⚙️ Apply Selection</button>
</div>
</div>
</div>
</div>
<!-- Status Info -->
<div id="camera-status-info" class="status-info" style="display: none; animation: slideInDown 0.4s ease; background: var(--blue-100); border: 1px solid var(--blue-200); border-radius: var(--radius-md); padding: 1rem; margin-bottom: 1rem;">
<p style="color: var(--blue-900); font-weight: 600;">📊 Session Info</p>
<p style="color: var(--slate-700); margin-top: 0.5rem;">⏱️ Duration: <span id="session-duration" style="font-weight: 700;">00:00</span></p>
<p style="color: var(--slate-700); margin-top: 0.3rem;">🤖 Models: <span id="session-models" style="font-weight: 700;">-</span></p>
</div>
<!-- Main Layout -->
<div class="live-camera-layout reveal" style="animation: slideInUp 0.6s ease 0.3s both;">
<!-- Video Feed Section -->
<div class="video-section">
<div class="video-container" id="video-container">
<img id="camera-feed" src="" alt="Camera Feed" style="display: none; width: 100%; height: 100%; object-fit: cover;">
<div class="video-overlay" id="video-overlay" style="display: none;">
<div class="status-badge" id="status-badge">
<span class="status-dot active"></span>
<span>● LIVE</span>
</div>
</div>
<div id="camera-placeholder" style="text-align: center; color: var(--slate-500); padding: 2rem;">
<p style="font-size: 3rem; margin-bottom: 1rem; animation: bounce 2s ease-in-out infinite;">📷</p>
<p style="font-weight: 600;">Select detection models above and click "Start Camera"</p>
<p style="font-size: 0.9rem; margin-top: 0.5rem;">Real-time AI analysis will begin immediately</p>
</div>
</div>
<!-- Video Controls -->
<div class="video-controls">
<button id="start-btn" class="btn btn-success" onclick="startCamera()" style="display: block; flex: 1;" title="Start camera feed">
▶️ Start Camera
</button>
<button id="stop-btn" class="btn btn-danger" onclick="stopCamera()" style="display: none; flex: 1;" title="Stop camera feed">
⏹️ Stop Camera
</button>
<button id="record-btn" class="btn btn-secondary" onclick="toggleRecording()" style="display: none;" title="Record video feed">
🔴 Record
</button>
<button id="fullscreen-btn" class="btn btn-secondary" onclick="toggleFullscreen()" title="Toggle fullscreen mode">
⛶ Fullscreen
</button>
<button class="btn btn-secondary" onclick="openStorageManager()" title="View saved videos and images">
💾 Storage
</button>
</div>
</div>
<!-- Alerts and Stats Panel -->
<div class="alerts-panel">
<!-- Alerts Section -->
<div style="margin-bottom: 1.2rem;">
<h2 style="color: var(--blue-900); margin-bottom: 0.8rem;">🚨 Detection Alerts</h2>
<div class="alerts-container" id="alerts-container">
<div class="alert-placeholder">
<p style="color: var(--slate-700); margin-bottom: 0.3rem;">✓ No alerts detected</p>
<small style="color: var(--slate-500);">Alerts will appear here when unusual activity is detected</small>
</div>
</div>
</div>
<!-- Statistics Section -->
<div class="stats-container" style="background: var(--blue-100); border: 1px solid var(--blue-200); border-radius: var(--radius-md); padding: 1rem;">
<h3 style="color: var(--blue-900); margin-bottom: 0.8rem;">📊 Statistics</h3>
<div class="stat-item">
<span style="color: var(--slate-700);">Detections:</span>
<span class="stat-value" id="total-detections" style="color: var(--blue-700);">0</span>
</div>
<div class="stat-item">
<span style="color: var(--slate-700);">Alerts:</span>
<span class="stat-value" id="total-alerts" style="color: var(--danger);">0</span>
</div>
<div class="stat-item">
<span style="color: var(--slate-700);">👤 Person Detected:</span>
<span class="stat-value" id="person-detected" style="color: var(--blue-700);">0</span>
</div>
<div class="stat-item">
<span style="color: var(--slate-700);">👁️ Person Present:</span>
<span class="stat-value" id="person-present" style="color: var(--ok);">❌ No</span>
</div>
<div class="stat-item">
<span style="color: var(--slate-700);">Status:</span>
<span class="stat-value status-active" style="color: var(--ok);">🟢 Ready</span>
</div>
</div>
<!-- Pose Analysis Section -->
<div style="background: var(--orange-100); border: 1px solid var(--orange-500); border-radius: var(--radius-md); padding: 1rem; margin-top: 1rem;">
<h3 style="color: var(--orange-600); margin-bottom: 0.8rem;">🧍 Pose Analysis</h3>
<div class="stat-item">
<span style="color: var(--slate-700);">Risk Level:</span>
<span class="stat-value" id="pose-risk-level" style="color: var(--ok);">SAFE</span>
</div>
<div class="stat-item">
<span style="color: var(--slate-700);">Action:</span>
<span class="stat-value" id="pose-action" style="color: var(--slate-700);"></span>
</div>
<div class="stat-item">
<span style="color: var(--slate-700);">Score:</span>
<span class="stat-value" id="pose-score" style="color: var(--blue-700);">0.00</span>
</div>
</div>
</div>
</div>
<!-- Info Box -->
<div class="info-box reveal" style="animation: slideInUp 0.6s ease 0.4s both; margin-top: 1.5rem;">
<h3 style="color: var(--blue-900); margin-bottom: 0.8rem;">ℹ️ How Live Monitoring Works</h3>
<ul style="color: var(--slate-700); line-height: 1.8;">
<li>✓ The camera feed is processed in real-time using multiple AI models</li>
<li>✓ Objects, activities, and unusual behavior are automatically detected</li>
<li>✓ High-priority alerts are highlighted and logged instantly</li>
<li>✓ All detections are recorded for your review and analysis</li>
<li>✓ Multiple cameras can be monitored simultaneously</li>
</ul>
</div>
</div>
<!-- Storage Drawer -->
<div id="storage-backdrop" onclick="closeStorageDrawer()"></div>
<div id="storage-drawer" class="storage-drawer">
<div class="sd-header">
<div class="sd-title-row">
<span class="sd-title">💾 Storage</span>
<button class="sd-close" onclick="closeStorageDrawer()" title="Close"></button>
</div>
<div class="sd-stats-row" id="sd-stats-row">
<div class="sd-stat">Loading…</div>
</div>
<div class="sd-tabs">
<button class="sd-tab active" id="sd-tab-sessions" onclick="switchStorageTab('sessions')">🎥 Sessions</button>
<button class="sd-tab" id="sd-tab-recordings" onclick="switchStorageTab('recordings')">🎬 Recordings</button>
<button class="sd-tab" id="sd-tab-images" onclick="switchStorageTab('images')">📷 Images</button>
</div>
</div>
<div class="sd-body" id="sd-body">
<!-- Inline video player (hidden by default) -->
<div id="sd-inline-player">
<div class="sd-player-close">
<button onclick="closeInlinePlayer()">✕ Close player</button>
</div>
<video id="sd-video-el" controls></video>
</div>
<!-- Tab content injected by JS -->
<div id="sd-content"></div>
</div>
</div>
<!-- Load UI Utilities and Live Camera Script -->
<script src="{{ url_for('static', filename='js/ui-utils.js') }}"></script>
<script src="{{ url_for('static', filename='js/live_camera.js') }}"></script>
<script>
// Mobile menu toggle
const hamburger = document.getElementById('hamburger-toggle');
const navMenu = document.getElementById('nav-menu');
if (hamburger) {
hamburger.addEventListener('click', function(e) {
e.stopPropagation();
hamburger.classList.toggle('active');
navMenu.classList.toggle('active');
});
}
// Close menu when clicking on a link
document.querySelectorAll('.nav-link, .nav-menu a').forEach(link => {
link.addEventListener('click', function() {
hamburger?.classList.remove('active');
navMenu?.classList.remove('active');
});
});
// Close menu when clicking outside
document.addEventListener('click', function(e) {
if (!e.target.closest('.nav-container')) {
hamburger?.classList.remove('active');
navMenu?.classList.remove('active');
}
});
// Dropdown toggle for mobile
function toggleDropdown(e) {
e.preventDefault();
const dropdown = e.target.closest('.dropdown');
dropdown.classList.toggle('active');
}
</script>
</body>
</html>