feat: proxy HLS through backend to eliminate CORS issues

This commit is contained in:
tian 2026-05-07 15:03:52 +08:00
parent 3930f014df
commit d730413cba
2 changed files with 36 additions and 3 deletions

View File

@ -662,6 +662,7 @@ func (u *UI) Routes() (chi.Router, error) {
r.Get("/diagnostics", u.pageDiagnostics)
r.Get("/alarms", u.pageAlarms)
r.Get("/monitor", u.pageMonitor)
r.Get("/hls/{deviceID}/*", u.proxyHLS)
r.Get("/api/monitor/channels", u.apiMonitorChannels)
r.Get("/recognition", u.pageRecognition)
r.Get("/logs", u.pageLogs)
@ -3891,3 +3892,33 @@ func (u *UI) apiMonitorChannels(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(code)
w.Write(body)
}
func (u *UI) proxyHLS(w http.ResponseWriter, r *http.Request) {
deviceID := chi.URLParam(r, "deviceID")
dev, ok := u.findDevice(deviceID)
if !ok {
http.Error(w, "device not found", http.StatusNotFound)
return
}
hlsPath := chi.URLParam(r, "*")
if hlsPath == "" {
http.Error(w, "missing path", http.StatusBadRequest)
return
}
body, code, err := u.agent.Do("GET", dev.IP, dev.MediaPort, "/"+hlsPath, nil)
if err != nil {
http.Error(w, err.Error(), http.StatusBadGateway)
return
}
// Set HLS-friendly headers
if strings.HasSuffix(hlsPath, ".m3u8") {
w.Header().Set("Content-Type", "application/vnd.apple.mpegurl")
w.Header().Set("Access-Control-Allow-Origin", "*")
} else if strings.HasSuffix(hlsPath, ".ts") {
w.Header().Set("Content-Type", "video/mp2t")
w.Header().Set("Access-Control-Allow-Origin", "*")
}
w.Header().Set("Cache-Control", "no-cache")
w.WriteHeader(code)
w.Write(body)
}

View File

@ -34,7 +34,7 @@ function loadAll() {
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; return ch; }); })
.then(function(data) { return (data.channels||[]).map(function(ch) { ch._dev = dev.name; ch._devId = dev.id; return ch; }); })
.catch(function() { return []; });
});
@ -49,6 +49,7 @@ function loadAll() {
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>';
@ -63,12 +64,13 @@ function loadAll() {
wall.innerHTML = html;
// Initialize HLS players
all.forEach(function(ch, i) {
if (!ch.hls_url) return;
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(ch.hls_url);
hls.loadSource(proxyUrl);
hls.attachMedia(video);
} else if (video.canPlayType('application/vnd.apple.mpegurl')) {
video.src = ch.hls_url;