Spaces:
Runtime error
Runtime error
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Live Video Feed with Alerts</title> | |
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> | |
<meta http-equiv="Pragma" content="no-cache" /> | |
<meta http-equiv="Expires" content="0" /> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
<style> | |
:root { | |
--primary-color: #2ecc71; | |
--primary-dark: #27ae60; | |
--secondary-color: #1e8449; | |
--accent-color: #3498db; | |
--background-color: #f5f5f5; | |
--card-color: #ffffff; | |
--text-color: #333333; | |
--text-light: #7f8c8d; | |
--border-color: #e0e0e0; | |
--shadow: 0 4px 6px rgba(0, 0, 0, 0.1); | |
--border-radius: 8px; | |
} | |
* { | |
margin: 0; | |
padding: 0; | |
box-sizing: border-box; | |
} | |
body { | |
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
background-color: var(--background-color); | |
color: var(--text-color); | |
display: flex; | |
flex-direction: column; | |
min-height: 100vh; | |
} | |
.header { | |
background-color: var(--primary-color); | |
color: white; | |
padding: 1rem 2rem; | |
display: flex; | |
justify-content: space-between; | |
align-items: center; | |
box-shadow: var(--shadow); | |
} | |
.header h1 { | |
font-size: 1.5rem; | |
margin: 0; | |
font-weight: 600; | |
} | |
.container { | |
display: flex; | |
flex: 1; | |
} | |
.sidebar { | |
width: 300px; | |
background-color: var(--card-color); | |
border-right: 1px solid var(--border-color); | |
padding: 1.5rem; | |
overflow-y: auto; | |
transition: all 0.3s ease; | |
box-shadow: 2px 0 5px rgba(0, 0, 0, 0.05); | |
} | |
.sidebar-header { | |
display: flex; | |
justify-content: space-between; | |
align-items: center; | |
margin-bottom: 1.5rem; | |
padding-bottom: 0.75rem; | |
border-bottom: 2px solid var(--primary-color); | |
} | |
.main-content { | |
flex: 1; | |
padding: 2rem; | |
overflow-y: auto; | |
display: flex; | |
flex-direction: column; | |
gap: 1.5rem; | |
} | |
.section-title { | |
color: var(--primary-dark); | |
font-size: 1.2rem; | |
margin-bottom: 1rem; | |
padding-bottom: 0.5rem; | |
border-bottom: 2px solid var(--primary-color); | |
} | |
.card { | |
background-color: var(--card-color); | |
border-radius: var(--border-radius); | |
box-shadow: var(--shadow); | |
padding: 1.5rem; | |
margin-bottom: 1.5rem; | |
border-top: 4px solid var(--primary-color); | |
} | |
.video-container { | |
position: relative; | |
overflow: hidden; | |
border-radius: var(--border-radius); | |
background-color: #000; | |
box-shadow: var(--shadow); | |
} | |
.video-feed { | |
width: 100%; | |
border-radius: var(--border-radius); | |
display: block; | |
border: none; | |
} | |
.status { | |
position: absolute; | |
top: 15px; | |
right: 15px; | |
background-color: rgba(0, 0, 0, 0.6); | |
color: white; | |
padding: 0.5rem 0.75rem; | |
border-radius: 20px; | |
font-size: 0.9rem; | |
display: flex; | |
align-items: center; | |
gap: 8px; | |
} | |
.status-dot { | |
height: 10px; | |
width: 10px; | |
background-color: var(--primary-color); | |
border-radius: 50%; | |
display: inline-block; | |
animation: pulse 1.5s infinite; | |
} | |
@keyframes pulse { | |
0% { opacity: 1; } | |
50% { opacity: 0.5; } | |
100% { opacity: 1; } | |
} | |
.detection-list { | |
list-style: none; | |
margin-top: 0.5rem; | |
} | |
.detection-item { | |
display: flex; | |
justify-content: space-between; | |
padding: 0.75rem; | |
border-bottom: 1px solid var(--border-color); | |
transition: background-color 0.2s ease; | |
} | |
.detection-item:hover { | |
background-color: rgba(46, 204, 113, 0.1); | |
} | |
.detection-item:last-child { | |
border-bottom: none; | |
} | |
.detection-count { | |
background-color: var(--primary-color); | |
color: white; | |
padding: 2px 8px; | |
border-radius: 12px; | |
font-size: 0.9rem; | |
} | |
.alert-info { | |
background-color: rgba(46, 204, 113, 0.1); | |
border-left: 4px solid var(--primary-color); | |
padding: 1rem; | |
margin-top: 1rem; | |
border-radius: 4px; | |
display: flex; | |
align-items: center; | |
gap: 10px; | |
} | |
.alert-info i { | |
color: var(--primary-color); | |
font-size: 1.2rem; | |
} | |
#graph { | |
height: 300px; | |
margin-top: 1rem; | |
} | |
/* Responsive styles */ | |
@media (max-width: 992px) { | |
.container { | |
flex-direction: column; | |
} | |
.sidebar { | |
width: 100%; | |
border-right: none; | |
border-bottom: 1px solid var(--border-color); | |
} | |
} | |
@media (max-width: 576px) { | |
.header { | |
padding: 1rem; | |
} | |
.main-content, .sidebar { | |
padding: 1rem; | |
} | |
} | |
</style> | |
</head> | |
<body> | |
<header class="header"> | |
<h1><i class="fas fa-video"></i> Security Monitoring System</h1> | |
<div> | |
<span id="current-time"></span> | |
</div> | |
</header> | |
<div class="container"> | |
<div class="sidebar"> | |
<div class="sidebar-header"> | |
<h2>Detection Summary</h2> | |
</div> | |
<div class="card"> | |
<div class="section-title">Detected Objects</div> | |
<ul id="class-list" class="detection-list"> | |
<!-- List of detected classes will be populated here --> | |
<li class="detection-item">Loading data...</li> | |
</ul> | |
</div> | |
<div class="card"> | |
<div class="section-title">Detection Graph</div> | |
<div id="graph"></div> | |
</div> | |
</div> | |
<div class="main-content"> | |
<div class="card"> | |
<div class="section-title">Live Camera Feed</div> | |
<div class="video-container"> | |
<img id="video-feed" class="video-feed" src="{{ url_for('video_feed') }}" alt="Live Video Feed"> | |
<div class="status"> | |
<span class="status-dot"></span> | |
Live | |
</div> | |
</div> | |
<div class="alert-info"> | |
<i class="fas fa-bell"></i> | |
<p>If a person is detected, a call will be initiated and an alert will be sent via Telegram.</p> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- Include Plotly.js for interactive graphs --> | |
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script> | |
<script> | |
// Display current time | |
function updateTime() { | |
const now = new Date(); | |
document.getElementById('current-time').textContent = now.toLocaleTimeString(); | |
} | |
setInterval(updateTime, 1000); | |
updateTime(); | |
// Fetch detection data from the server and update the dashboard | |
async function fetchDetectionData() { | |
try { | |
const response = await fetch('/detection_data'); | |
const data = await response.json(); | |
updateClassList(data); | |
updateGraph(data); | |
} catch (error) { | |
console.error("Error fetching detection data:", error); | |
document.getElementById('class-list').innerHTML = '<li class="detection-item">Error loading data</li>'; | |
} | |
} | |
// Update the class list in the sidebar | |
function updateClassList(data) { | |
const classList = document.getElementById('class-list'); | |
classList.innerHTML = ''; | |
if (Object.keys(data).length === 0) { | |
const li = document.createElement('li'); | |
li.className = 'detection-item'; | |
li.textContent = 'No objects detected'; | |
classList.appendChild(li); | |
return; | |
} | |
for (const [cls, count] of Object.entries(data)) { | |
const li = document.createElement('li'); | |
li.className = 'detection-item'; | |
const className = document.createElement('span'); | |
className.textContent = cls; | |
const countSpan = document.createElement('span'); | |
countSpan.className = 'detection-count'; | |
countSpan.textContent = count; | |
li.appendChild(className); | |
li.appendChild(countSpan); | |
classList.appendChild(li); | |
} | |
} | |
// Update the detection graph using Plotly | |
function updateGraph(data) { | |
const classes = Object.keys(data); | |
const counts = Object.values(data); | |
const trace = { | |
x: classes, | |
y: counts, | |
type: 'bar', | |
marker: { | |
color: 'rgba(46, 204, 113, 0.7)', | |
line: { | |
color: 'rgba(39, 174, 96, 1)', | |
width: 1.5 | |
} | |
} | |
}; | |
const layout = { | |
margin: { t: 20, r: 20, l: 40, b: 60 }, | |
xaxis: { title: 'Objects' }, | |
yaxis: { title: 'Count' }, | |
font: { family: 'Segoe UI, sans-serif' }, | |
paper_bgcolor: 'rgba(0,0,0,0)', | |
plot_bgcolor: 'rgba(0,0,0,0)', | |
}; | |
Plotly.newPlot('graph', [trace], layout, {responsive: true}); | |
} | |
// Periodically fetch detection data every 5 seconds | |
setInterval(fetchDetectionData, 5000); | |
// Initial fetch | |
fetchDetectionData(); | |
</script> | |
</body> | |
</html> |