Alfred/templates/index.html.old
2025-11-11 13:38:25 +01:00

169 lines
7.2 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="fr">
<head>
<meta charset="utf-8" />
<title>Caméra USB Détection & Volume</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style>
html,body { margin:0; padding:0; background:#111; color:#eee; font-family:system-ui,Segoe UI,Roboto,Arial,sans-serif; }
header { padding:12px 16px; background:#1b1b1b; position:sticky; top:0 }
.wrap { max-width:1000px; margin:0 auto; padding:16px }
.video { display:block; width:100%; height:auto; border-radius:16px; box-shadow:0 6px 24px rgba(0,0,0,.35); background:#000 }
.row { display:flex; gap:12px; flex-wrap:wrap; margin-top:12px; align-items:center }
.btn { background:#2c7be5; border:none; color:#fff; padding:10px 14px; border-radius:10px; cursor:pointer; text-decoration:none }
.btn.secondary { background:#444 }
.btn:active { transform:translateY(1px) }
.card { background:#151515; border-radius:16px; padding:14px; box-shadow:0 10px 30px rgba(0,0,0,.35); }
.slider { width:240px }
.badge { font-size:.85rem; opacity:.85; }
.muted { color:#f39; }
code { background:#1e1e1e; padding:2px 6px; border-radius:6px; }
.pill { padding:4px 8px; border-radius:999px; background:#222; }
</style>
</head>
<body>
<header><strong>Diffusion Caméra USB</strong></header>
<div class="wrap">
<!-- Boutons AU-DESSUS de la vidéo -->
<div class="row" style="margin-top:2px">
<a class="btn" href="{{ url_for('snapshot') }}" target="_blank">📸 Snapshot</a>
<button id="btnToggleDetect" class="btn">{{ '🟢 Détection ON' if detect_enabled else '⚪ Détection OFF' }}</button>
<button id="btnToggleFps" class="btn secondary">{{ '🙈 Cacher FPS' if show_fps else '👁️ Afficher FPS' }}</button>
<!-- Petites infos à côté -->
<span class="pill">Détecteur: <strong id="detBackend">{{ detector_backend }}</strong></span>
<span class="pill">Status: <strong id="detStatus">{{ 'ON' if detect_enabled else 'OFF' }}</strong></span>
<span class="pill">FPS: <strong id="fpsText">-- / --</strong></span>
</div>
<!-- Vidéo -->
<img class="video" src="{{ url_for('video_feed') }}" alt="Flux caméra (MJPEG)" />
<!-- Volume -->
<div class="row" style="margin-top:18px">
<div class="card">
<div class="row">
<button id="volDown" class="btn" title="-5%"></button>
<input id="volSlider" class="slider" type="range" min="0" max="150" step="1" />
<button id="volUp" class="btn" title="+5%"></button>
<button id="volMute" class="btn">🔇 Mute</button>
</div>
<div style="margin-top:8px">
Volume: <strong id="volLabel">--%</strong>
<span class="badge">backend: <code id="backend">{{ audio_backend }}</code></span>
<span id="mutedTag" class="badge muted" style="display:none">[muet]</span>
</div>
</div>
</div>
</div>
<script>
// -------- Volume UI --------
const slider = document.getElementById('volSlider');
const label = document.getElementById('volLabel');
const mutedTag = document.getElementById('mutedTag');
const backendEl = document.getElementById('backend');
const btnUp = document.getElementById('volUp');
const btnDown = document.getElementById('volDown');
const btnMute = document.getElementById('volMute');
let vol = {{ initial_volume|int }};
let muted = {{ initial_muted|tojson }};
let backend = backendEl.textContent || 'auto';
function renderVolume() {
slider.value = vol;
label.textContent = vol + '%';
mutedTag.style.display = (muted === true) ? 'inline' : 'none';
}
async function refreshVolume() {
try {
const r = await fetch('{{ url_for("api_get_volume") }}');
const j = await r.json();
if (typeof j.volume === 'number') vol = j.volume;
if (typeof j.muted === 'boolean') muted = j.muted;
if (j.backend) backendEl.textContent = j.backend;
renderVolume();
} catch(e) { /* ignore */ }
}
slider.addEventListener('change', async () => {
const val = parseInt(slider.value);
const r = await fetch('{{ url_for("api_set_volume") }}?level=' + val, { method:'POST' });
const j = await r.json();
vol = (typeof j.volume === 'number') ? j.volume : val;
muted = (typeof j.muted === 'boolean') ? j.muted : muted;
renderVolume();
});
btnUp.addEventListener('click', async () => {
const r = await fetch('{{ url_for("api_volume_up") }}', { method:'POST' });
const j = await r.json();
if (typeof j.volume === 'number') vol = j.volume;
if (typeof j.muted === 'boolean') muted = j.muted;
renderVolume();
});
btnDown.addEventListener('click', async () => {
const r = await fetch('{{ url_for("api_volume_down") }}', { method:'POST' });
const j = await r.json();
if (typeof j.volume === 'number') vol = j.volume;
if (typeof j.muted === 'boolean') muted = j.muted;
renderVolume();
});
btnMute.addEventListener('click', async () => {
const r = await fetch('{{ url_for("api_volume_mute") }}', { method:'POST' });
const j = await r.json();
if (typeof j.volume === 'number') vol = j.volume;
if (typeof j.muted === 'boolean') muted = j.muted;
renderVolume();
});
// -------- Détection / FPS UI --------
const detBackend = document.getElementById('detBackend');
const detStatus = document.getElementById('detStatus');
const btnToggleDetect = document.getElementById('btnToggleDetect');
const btnToggleFps = document.getElementById('btnToggleFps');
const fpsText = document.getElementById('fpsText');
async function refreshStats() {
try {
const r = await fetch('/stats');
const j = await r.json();
detBackend.textContent = j.backend || 'none';
detStatus.textContent = j.detect_enabled ? 'ON' : 'OFF';
fpsText.textContent = (j.cam_fps?.toFixed ? j.cam_fps.toFixed(1) : j.cam_fps) + ' / ' +
(j.infer_fps?.toFixed ? j.infer_fps.toFixed(1) : j.infer_fps);
btnToggleDetect.textContent = j.detect_enabled ? '🟢 Détection ON' : '⚪ Détection OFF';
btnToggleFps.textContent = j.show_fps ? '🙈 Cacher FPS' : '👁️ Afficher FPS';
} catch(e) { /* ignore */ }
}
btnToggleDetect.addEventListener('click', async () => {
const r = await fetch('/detect/toggle', { method:'POST' });
await r.json();
refreshStats();
});
btnToggleFps.addEventListener('click', async () => {
const r0 = await fetch('/stats');
const s = await r0.json();
const desired = !(s.show_fps === true);
const r = await fetch('/fps/show?state=' + (desired ? 'true' : 'false'), { method:'POST' });
await r.json();
refreshStats();
});
// init
renderVolume();
refreshStats();
setInterval(refreshVolume, 3000);
setInterval(refreshStats, 1000);
</script>
</body>
</html>