var / index.html
Sousa45's picture
Add 2 files
2a57e57 verified
Raw History Blame Contribute Delete
26.3 kB
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sistema VAR - Câmeras WiFi</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
.camera-view {
position: relative;
background-color: #111;
border: 2px solid #333;
overflow: hidden;
transition: all 0.3s ease;
}
.camera-view::before {
content: "";
display: block;
padding-top: 56.25%; /* 16:9 Aspect Ratio */
}
.camera-content {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
display: flex;
justify-content: center;
align-items: center;
color: white;
font-size: 1.5rem;
}
.offside-line {
position: absolute;
height: 2px;
width: 100%;
background-color: red;
z-index: 10;
}
.player-marker {
position: absolute;
width: 12px;
height: 12px;
border-radius: 50%;
background-color: yellow;
z-index: 20;
}
.selected-camera {
border: 3px solid #3B82F6;
box-shadow: 0 0 15px rgba(59, 130, 246, 0.5);
}
.decision-panel {
transition: all 0.3s ease;
max-width: 800px;
margin: 0 auto;
}
.wifi-strength {
position: absolute;
top: 10px;
right: 10px;
display: flex;
align-items: center;
background-color: rgba(0,0,0,0.7);
padding: 2px 5px;
border-radius: 10px;
font-size: 12px;
}
.wifi-icon {
margin-right: 3px;
}
.wifi-excellent { color: #4ade80; }
.wifi-good { color: #a3e635; }
.wifi-fair { color: #facc15; }
.wifi-weak { color: #f97316; }
.wifi-poor { color: #ef4444; }
.replay-timeline {
-webkit-appearance: none;
width: 100%;
height: 6px;
background: #4b5563;
border-radius: 3px;
margin-top: 8px;
}
.replay-timeline::-webkit-slider-thumb {
-webkit-appearance: none;
width: 16px;
height: 16px;
background: #3b82f6;
border-radius: 50%;
cursor: pointer;
}
.camera-feed {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
.camera-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.3);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: white;
}
</style>
</head>
<body class="bg-gray-900 text-white">
<div class="container mx-auto px-4 py-6">
<!-- Header -->
<header class="flex justify-between items-center mb-6">
<div>
<h1 class="text-3xl font-bold text-blue-400">
<i class="fas fa-video mr-2"></i>Sistema VAR WiFi
</h1>
<p class="text-gray-400">Análise de Impedimento - Câmeras Sem Fio</p>
</div>
<div class="flex items-center space-x-4">
<button id="scan-cameras" class="bg-green-600 hover:bg-green-700 px-4 py-2 rounded-lg">
<i class="fas fa-wifi mr-2"></i>Procurar Câmeras
</button>
<button id="toggle-panel" class="bg-blue-600 hover:bg-blue-700 px-4 py-2 rounded-lg">
<i class="fas fa-sliders-h mr-2"></i>Painel
</button>
</div>
</header>
<!-- Camera Grid -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 mb-6" id="camera-grid">
<!-- Cameras will be added dynamically -->
<div class="text-center py-10 text-gray-500" id="no-cameras">
<i class="fas fa-wifi-slash text-4xl mb-3"></i>
<p>Nenhuma câmera conectada</p>
<button onclick="scanCameras()" class="mt-4 bg-blue-600 hover:bg-blue-700 px-4 py-2 rounded-lg">
<i class="fas fa-sync-alt mr-2"></i>Procurar Câmeras
</button>
</div>
</div>
<!-- Selected Camera View -->
<div class="mb-6 bg-gray-800 rounded-lg p-4">
<div class="flex justify-between items-center mb-4">
<h2 class="text-xl font-semibold">Visualização Selecionada</h2>
<div class="flex items-center space-x-3">
<span id="camera-fps" class="text-sm bg-gray-700 px-2 py-1 rounded">0 FPS</span>
<span id="camera-resolution" class="text-sm bg-gray-700 px-2 py-1 rounded">--</span>
<span id="camera-latency" class="text-sm bg-gray-700 px-2 py-1 rounded">0ms</span>
</div>
</div>
<div class="camera-view" id="selected-camera-view">
<div class="camera-overlay">
<i class="fas fa-eye text-gray-600 text-4xl mb-2"></i>
<p>Selecione uma câmera para visualizar</p>
</div>
</div>
<!-- Enhanced Replay Controls -->
<div class="mt-4">
<div class="flex justify-between items-center mb-1">
<span class="text-sm text-gray-400">00:00:00</span>
<span class="text-sm text-gray-400" id="replay-duration">00:00:10</span>
</div>
<input type="range" min="0" max="100" value="0" class="replay-timeline" id="replay-timeline">
<div class="flex justify-between bg-gray-700 p-3 rounded-lg mt-2">
<button onclick="skipToStart()" class="text-white hover:text-blue-400 px-2">
<i class="fas fa-fast-backward"></i>
</button>
<button onclick="frameBackward()" class="text-white hover:text-blue-400 px-2">
<i class="fas fa-step-backward"></i>
</button>
<button onclick="playPause()" class="text-white hover:text-blue-400 px-4">
<i class="fas fa-play" id="play-icon"></i>
</button>
<button onclick="frameForward()" class="text-white hover:text-blue-400 px-2">
<i class="fas fa-step-forward"></i>
</button>
<button onclick="skipToEnd()" class="text-white hover:text-blue-400 px-2">
<i class="fas fa-fast-forward"></i>
</button>
<button onclick="slowMotion()" class="text-white hover:text-blue-400 px-2">
<i class="fas fa-tachometer-alt"></i>
</button>
<div class="flex items-center ml-2">
<span class="text-sm mr-2">Velocidade:</span>
<select id="playback-speed" class="bg-gray-600 text-white text-sm rounded px-2 py-1">
<option value="0.25">0.25x</option>
<option value="0.5">0.5x</option>
<option value="1" selected>1x</option>
<option value="2">2x</option>
<option value="4">4x</option>
</select>
</div>
</div>
</div>
</div>
<!-- Decision Panel -->
<div id="decision-panel" class="decision-panel w-full bg-gray-800 rounded-lg p-4 mb-6">
<div class="flex justify-between items-center mb-4">
<h2 class="text-xl font-semibold">Painel de Decisão</h2>
<div class="flex items-center space-x-2">
<span class="text-sm">Bateria:</span>
<div class="w-24 bg-gray-700 h-2 rounded-full overflow-hidden">
<div id="camera-battery" class="bg-green-500 h-full" style="width: 80%"></div>
</div>
<span id="battery-percent" class="text-sm">80%</span>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<!-- Offside Analysis -->
<div>
<h3 class="text-lg font-medium mb-2">Análise de Impedimento</h3>
<div class="bg-gray-700 p-3 rounded-lg mb-3">
<div class="flex justify-between mb-2">
<span>Último defensor:</span>
<span id="last-defender">--</span>
</div>
<div class="flex justify-between mb-2">
<span>Atacante:</span>
<span id="attacker-pos">--</span>
</div>
<div class="flex justify-between font-bold">
<span>Diferença:</span>
<span id="offside-diff">--</span>
</div>
</div>
<div class="flex space-x-2">
<button onclick="takeScreenshot()" class="flex-1 bg-blue-600 hover:bg-blue-700 py-2 rounded">
<i class="fas fa-camera mr-2"></i>Capturar
</button>
</div>
</div>
<!-- Decision Buttons -->
<div>
<h3 class="text-lg font-medium mb-2">Decisão</h3>
<div class="grid grid-cols-2 gap-2">
<button onclick="makeDecision('goal')" class="bg-green-600 hover:bg-green-700 py-3 rounded-lg">
<i class="fas fa-check-circle mr-2"></i>Gol Válido
</button>
<button onclick="makeDecision('offside')" class="bg-red-600 hover:bg-red-700 py-3 rounded-lg">
<i class="fas fa-times-circle mr-2"></i>Impedimento
</button>
<button onclick="makeDecision('no-offside')" class="bg-blue-600 hover:bg-blue-700 py-3 rounded-lg">
<i class="fas fa-check mr-2"></i>Sem Impedimento
</button>
<button onclick="makeDecision('inconclusive')" class="bg-gray-600 hover:bg-gray-700 py-3 rounded-lg">
<i class="fas fa-question mr-2"></i>Inconclusivo
</button>
</div>
</div>
</div>
<!-- Decision Log -->
<div class="mt-6">
<h3 class="text-lg font-medium mb-2">Histórico de Decisões</h3>
<div class="bg-gray-700 p-3 rounded-lg max-h-40 overflow-y-auto">
<div class="text-sm" id="decision-log">
<!-- Decisions will be added here -->
</div>
</div>
</div>
</div>
<!-- Status Bar -->
<footer class="mt-6 bg-gray-800 p-3 rounded-lg flex justify-between items-center">
<div class="flex items-center space-x-4">
<div class="flex items-center">
<div id="wifi-status" class="w-3 h-3 rounded-full bg-green-500 mr-2"></div>
<span id="wifi-status-text">Conectado</span>
</div>
</div>
<div class="flex items-center space-x-4">
<div>
<span>Câmeras: <span id="connected-cameras" class="font-semibold">0</span>/<span id="total-cameras">6</span></span>
</div>
</div>
</footer>
</div>
<script>
// Camera data
const cameraDatabase = [
{ id: 1, name: "Visão Geral", ip: "192.168.1.101", wifiStrength: 4, fps: 60, resolution: "1080p", battery: 85 },
{ id: 2, name: "Lateral Esquerda", ip: "192.168.1.102", wifiStrength: 3, fps: 50, resolution: "1080p", battery: 72 },
{ id: 3, name: "Lateral Direita", ip: "192.168.1.103", wifiStrength: 2, fps: 50, resolution: "1080p", battery: 65 },
{ id: 4, name: "Linha de Fundo", ip: "192.168.1.104", wifiStrength: 4, fps: 60, resolution: "4K", battery: 90 },
{ id: 5, name: "Zoom Ataque", ip: "192.168.1.105", wifiStrength: 3, fps: 60, resolution: "4K", battery: 78 },
{ id: 6, name: "Zoom Defesa", ip: "192.168.1.106", wifiStrength: 1, fps: 30, resolution: "720p", battery: 45 }
];
let connectedCameras = [];
let selectedCamera = null;
let isPlaying = false;
let currentTime = 0;
let replayInterval;
// Initialize
document.addEventListener('DOMContentLoaded', function() {
updateCameraStatus();
updateDecisionLog();
// Add event listeners
document.getElementById('scan-cameras').addEventListener('click', scanCameras);
document.getElementById('replay-timeline').addEventListener('input', function(e) {
currentTime = parseInt(e.target.value);
});
});
// Scan for WiFi cameras
function scanCameras() {
document.getElementById('no-cameras').classList.add('hidden');
const cameraGrid = document.getElementById('camera-grid');
cameraGrid.innerHTML = '';
// Simulate scanning
const scanning = document.createElement('div');
scanning.className = 'col-span-full text-center py-10 text-gray-500';
scanning.innerHTML = `
<i class="fas fa-wifi fa-spin text-4xl mb-3"></i>
<p>Procurando câmeras WiFi...</p>
`;
cameraGrid.appendChild(scanning);
// After delay, show found cameras
setTimeout(() => {
cameraGrid.innerHTML = '';
cameraDatabase.forEach(camera => {
const wifiClass = getWifiStrengthClass(camera.wifiStrength);
const wifiIcon = getWifiStrengthIcon(camera.wifiStrength);
const cameraElement = document.createElement('div');
cameraElement.className = 'camera-view cursor-pointer relative';
cameraElement.id = `camera-${camera.id}`;
cameraElement.onclick = () => selectCamera(camera.id);
cameraElement.innerHTML = `
<div class="camera-content">
<div class="text-center">
<i class="fas fa-video text-gray-600 text-4xl mb-2"></i>
<p>Câmera ${camera.id} - ${camera.name}</p>
<p class="text-sm text-gray-400 mt-1">${camera.ip}</p>
</div>
</div>
<div class="wifi-strength">
<i class="wifi-icon ${wifiClass} ${wifiIcon}"></i>
<span>${getWifiStrengthText(camera.wifiStrength)}</span>
</div>
`;
cameraGrid.appendChild(cameraElement);
});
connectedCameras = [...cameraDatabase];
updateCameraStatus();
}, 2000);
}
// Camera selection
function selectCamera(cameraNumber) {
// Remove selection from all cameras
document.querySelectorAll('.camera-view').forEach(cam => {
cam.classList.remove('selected-camera');
});
// Add selection to clicked camera
const cameraElement = document.getElementById(`camera-${cameraNumber}`);
cameraElement.classList.add('selected-camera');
selectedCamera = cameraNumber;
// Update selected camera view
const selectedView = document.getElementById('selected-camera-view');
selectedView.innerHTML = `
<div class="camera-overlay">
<i class="fas fa-circle-notch fa-spin text-4xl mb-2"></i>
<p>Conectando à câmera ${cameraNumber}...</p>
</div>
`;
// Find camera data
const camera = cameraDatabase.find(c => c.id === cameraNumber);
// After delay, show camera feed (simulated)
setTimeout(() => {
selectedView.innerHTML = `
<img src="https://via.placeholder.com/800x450/333/666?text=C%C3%A2mera+${cameraNumber}+${camera.resolution}"
class="camera-feed" alt="Camera Feed" id="camera-feed-${cameraNumber}">
<div class="absolute top-0 left-0 p-2 bg-black bg-opacity-70 text-sm">
CAM ${cameraNumber} - ${camera.name} | ${camera.resolution}
</div>
`;
// Update camera info
document.getElementById('camera-fps').textContent = `${camera.fps} FPS`;
document.getElementById('camera-resolution').textContent = camera.resolution;
document.getElementById('camera-latency').textContent = `${Math.floor(Math.random() * 50) + 10}ms`;
document.getElementById('camera-battery').style.width = `${camera.battery}%`;
document.getElementById('battery-percent').textContent = `${camera.battery}%`;
// Update battery color based on level
const batteryElement = document.getElementById('camera-battery');
if (camera.battery > 60) {
batteryElement.className = 'bg-green-500 h-full';
} else if (camera.battery > 30) {
batteryElement.className = 'bg-yellow-500 h-full';
} else {
batteryElement.className = 'bg-red-500 h-full';
}
}, 1000);
}
// WiFi strength helpers
function getWifiStrengthClass(strength) {
switch(strength) {
case 4: return 'wifi-excellent';
case 3: return 'wifi-good';
case 2: return 'wifi-fair';
case 1: return 'wifi-weak';
default: return 'wifi-poor';
}
}
function getWifiStrengthIcon(strength) {
switch(strength) {
case 4: return 'fa-wifi';
case 3: return 'fa-wifi';
case 2: return 'fa-wifi';
case 1: return 'fa-wifi';
default: return 'fa-wifi';
}
}
function getWifiStrengthText(strength) {
switch(strength) {
case 4: return 'Excelente';
case 3: return 'Boa';
case 2: return 'Média';
case 1: return 'Fraca';
default: return 'Ruim';
}
}
// Update camera connection status
function updateCameraStatus() {
document.getElementById('connected-cameras').textContent = connectedCameras.length;
document.getElementById('total-cameras').textContent = cameraDatabase.length;
if (connectedCameras.length === 0) {
document.getElementById('wifi-status').className = 'w-3 h-3 rounded-full bg-red-500 mr-2';
document.getElementById('wifi-status-text').textContent = 'Desconectado';
} else {
document.getElementById('wifi-status').className = 'w-3 h-3 rounded-full bg-green-500 mr-2';
document.getElementById('wifi-status-text').textContent = 'Conectado';
}
}
// Decision functions
function makeDecision(decision) {
const decisions = {
'goal': { text: 'Gol Válido', icon: 'fas fa-check-circle', color: 'green' },
'offside': { text: 'Impedimento', icon: 'fas fa-times-circle', color: 'red' },
'no-offside': { text: 'Sem Impedimento', icon: 'fas fa-check', color: 'blue' },
'inconclusive': { text: 'Inconclusivo', icon: 'fas fa-question', color: 'gray' }
};
const selected = decisions[decision];
if (!selected) return;
// Show confirmation
alert(`Decisão registrada: ${selected.text}`);
// Add to decision log
const log = document.getElementById('decision-log');
const newEntry = document.createElement('div');
newEntry.className = `flex justify-between py-1 ${log.children.length ? 'border-t border-gray-600' : ''}`;
newEntry.innerHTML = `
<span>${getCurrentTime()} - ${selected.text}</span>
<span class="text-${selected.color}-400">
${decision === 'goal' || decision === 'no-offside' ? '✓' : '×'}
</span>
`;
log.insertBefore(newEntry, log.firstChild);
}
function updateDecisionLog() {
const decisions = [
{ time: '45\'', text: 'Gol validado', result: '✓', color: 'green' },
{ time: '32\'', text: 'Impedimento', result: '×', color: 'red' },
{ time: '15\'', text: 'Sem impedimento', result: '✓', color: 'blue' }
];
const log = document.getElementById('decision-log');
log.innerHTML = '';
decisions.forEach(decision => {
const entry = document.createElement('div');
entry.className = `flex justify-between py-1 ${log.children.length ? 'border-t border-gray-600' : ''}`;
entry.innerHTML = `
<span>${decision.time} - ${decision.text}</span>
<span class="text-${decision.color}-400">${decision.result}</span>
`;
log.appendChild(entry);
});
}
function getCurrentTime() {
const now = new Date();
return `${now.getHours()}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}`;
}
// Replay controls
function playPause() {
const icon = document.getElementById('play-icon');
if (icon.classList.contains('fa-play')) {
icon.classList.remove('fa-play');
icon.classList.add('fa-pause');
isPlaying = true;
// Simulate playback
replayInterval = setInterval(() => {
currentTime += 1;
if (currentTime > 100) currentTime = 100;
document.getElementById('replay-timeline').value = currentTime;
}, 100);
} else {
icon.classList.remove('fa-pause');
icon.classList.add('fa-play');
isPlaying = false;
clearInterval(replayInterval);
}
}
function skipToStart() {
currentTime = 0;
document.getElementById('replay-timeline').value = currentTime;
if (isPlaying) {
clearInterval(replayInterval);
playPause();
}
}
function skipToEnd() {
currentTime = 100;
document.getElementById('replay-timeline').value = currentTime;
if (isPlaying) {
clearInterval(replayInterval);
playPause();
}
}
function frameForward() {
if (currentTime < 100) {
currentTime += 1;
document.getElementById('replay-timeline').value = currentTime;
}
}
function frameBackward() {
if (currentTime > 0) {
currentTime -= 1;
document.getElementById('replay-timeline').value = currentTime;
}
}
function slowMotion() {
const speedSelect = document.getElementById('playback-speed');
speedSelect.value = '0.25';
alert('Câmera lenta ativada (0.25x)');
}
function takeScreenshot() {
if (!selectedCamera) {
alert('Selecione uma câmera primeiro!');
return;
}
alert('Captura de tela salva no sistema');
// In a real implementation, this would save the current camera view
}
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Sousa45/var" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>