85 lines
3.3 KiB
HTML
85 lines
3.3 KiB
HTML
{{define "monitor"}}
|
|
<div class="card">
|
|
<div class="section-title">
|
|
<div>
|
|
<h2 class="title-with-icon">{{icon "devices"}}<span>视频监控</span></h2>
|
|
<div class="form-hint">所有在线设备的实时画面</div>
|
|
</div>
|
|
<div class="actions compact">
|
|
<button class="btn ghost" type="button" onclick="loadAll()">刷新</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="video-wall" style="display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:16px;margin-top:16px">
|
|
<div class="card muted" style="text-align:center;grid-column:1/-1">加载中…</div>
|
|
</div>
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
|
|
<script>
|
|
function loadAll() {
|
|
var wall = document.getElementById("video-wall");
|
|
wall.innerHTML = '<div class="card muted" style="text-align:center;grid-column:1/-1">加载中…</div>';
|
|
|
|
var devices = [
|
|
{{range .Devices}}{{if .Online}}
|
|
{id:"{{.DeviceID}}",name:"{{.DisplayName}}"},
|
|
{{end}}{{end}}
|
|
];
|
|
if (devices.length === 0) {
|
|
wall.innerHTML = '<div class="card muted" style="text-align:center;grid-column:1/-1">暂无在线设备</div>';
|
|
return;
|
|
}
|
|
|
|
var promises = devices.map(function(dev) {
|
|
return fetch('/ui/api/monitor/channels?device_id=' + encodeURIComponent(dev.id))
|
|
.then(function(r) { return r.json(); })
|
|
.then(function(data) { return (data.channels||[]).map(function(ch) { ch._dev = dev.name; ch._devId = dev.id; return ch; }); })
|
|
.catch(function() { return []; });
|
|
});
|
|
|
|
Promise.all(promises).then(function(results) {
|
|
var all = [];
|
|
results.forEach(function(chs) { all = all.concat(chs); });
|
|
if (all.length === 0) {
|
|
wall.innerHTML = '<div class="card muted" style="text-align:center;grid-column:1/-1">没有可预览的通道</div>';
|
|
return;
|
|
}
|
|
var cols = all.length <= 2 ? all.length : 2;
|
|
wall.style.gridTemplateColumns = "repeat(" + cols + ",minmax(0,1fr))";
|
|
var html = "";
|
|
all.forEach(function(ch, i) {
|
|
var proxyUrl = '/hls/' + ch._devId + '/hls/' + ch.name + '/index.m3u8';
|
|
html += '<div class="card" style="padding:8px">';
|
|
html += '<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:6px">';
|
|
html += '<span style="font-size:12px;font-weight:500">' + esc(ch._dev) + ' · ' + esc(ch.name) + '</span>';
|
|
html += '</div>';
|
|
if (ch.hls_url) {
|
|
html += '<video id="v' + i + '" controls autoplay muted style="width:100%;max-height:340px;background:#000"></video>';
|
|
} else {
|
|
html += '<div class="muted" style="height:200px;display:flex;align-items:center;justify-content:center">无预览地址</div>';
|
|
}
|
|
html += '</div>';
|
|
});
|
|
wall.innerHTML = html;
|
|
// Initialize HLS players
|
|
all.forEach(function(ch, i) {
|
|
if (!ch.hls_url || !ch._devId) return;
|
|
var proxyUrl = '/hls/' + ch._devId + '/hls/' + ch.name + '/index.m3u8';
|
|
var video = document.getElementById('v' + i);
|
|
if (!video) return;
|
|
if (Hls.isSupported()) {
|
|
var hls = new Hls();
|
|
hls.loadSource(proxyUrl);
|
|
hls.attachMedia(video);
|
|
} else if (video.canPlayType('application/vnd.apple.mpegurl')) {
|
|
video.src = ch.hls_url;
|
|
}
|
|
});
|
|
});
|
|
}
|
|
function esc(s) { return (s||"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,"""); }
|
|
loadAll();
|
|
</script>
|
|
{{end}}
|