window.onerror now captures column number and error stack trace when
available. Undefined or browser-sanitized "Script error." messages get a
descriptive placeholder instead of logging "undefined". Caller name
introspection is guarded against strict mode and empty names.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Integrate EventStream.js into montagereview.js for persistent MJPEG
streaming in replay mode (speeds >= 1x), falling back to per-frame
mode=single for speeds < 1x where zms lacks slow-motion support
- Add EventStream recovery logic: detect zms death via AJAX failures,
img.onerror, and error responses; auto-restart with new connkey and
exponential backoff (max 5 retries)
- Fix zms crash on bulk/interpolated frames: add stat() check in
sendFrame() for SaveJPEGs & 1 path, fall through to ffmpeg_input
when JPEG file doesn't exist on disk; send "No frame available"
text frame instead of terminating when no source available
- Center monitor canvases: text-align in CSS for scale mode,
horizontal offset calculation in maxfit2() for fit mode
- Reduce console noise: comment out high-frequency debug logs,
convert error-path logs to console.warn/error
- Fix ESLint sourceType to "script" for traditional non-module JS
files, resolving false-positive no-unused-vars on global functions
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
After adding the "players" variable, the "players is not defined" issue appears on various pages.
There's currently an error on the Zones page.
Yes, ideally, the variable should be added in *.js.php files on various pages.
The absence of the variable has no significant impact, other than incomplete display of information.
The current error disrupts all JavaScript.
This PR prevents the disruption of all JavaScript code on the page.
A detached Image() object does not reliably decode multipart MJPEG
streams — browsers only update a DOM-attached <img> with each frame
from a multipart/x-mixed-replace response. Switch to creating a
hidden DOM <img> element and use requestAnimationFrame to copy
decoded frames to the canvas.
Also properly clean up the img element from the DOM in stop() and
switchEvent(), and guard drawFrame() against drawing before the
first frame has been decoded.
Tested: start, pause, play, seek, setRate, stop all verified
working against a live zms event stream.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Introduce EventStream constructor that manages a persistent zms MJPEG
connection for event playback, replacing the per-frame mode=single HTTP
request pattern. Frames arrive via a hidden Image element and are drawn
to a caller-supplied canvas on each onload.
Supports seek, pause, play, rate control, scale changes, and event
switching via the existing zms command-socket protocol. Follows the
same constructor-function pattern as MonitorStream.js.
Include the script in montagereview.php for upcoming integration.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
this.closest() returns null when the <video-stream> element isn't inside
a monitor div, causing TypeError on the subsequent querySelector call.
Add null checks in both divMode and divError setters.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
MonitorStream.js initialized analyse_frames = true, causing zone
overlays to always appear on stream start regardless of view settings.
Change default to false so watch view starts clean. The zone editor
view already explicitly enables analysis after monitor creation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Avoid recreating a VolumeSlider (instead of destroying the existing one) if one already exists.
This will at least save resources and avoid assigning a repeat listener to "volumechange."
this.closest() returns null when the <video-stream> element isn't inside
a monitor div, causing TypeError on the subsequent querySelector call.
Add null checks in both divMode and divError setters.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
MonitorStream.js initialized analyse_frames = true, causing zone
overlays to always appear on stream start regardless of view settings.
Change default to false so watch view starts clean. The zone editor
view already explicitly enables analysis after monitor creation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The zone view enables analysis frames by default but the initial image
src and the rebuilt streaming URL were missing the analysis query param.
Add analysis=true to the getStreamHTML call in zone.php and preserve it
in MonitorStream.start() when switching from single to jpeg mode.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When StreamChannel is set to Restream, the JS maps it to the
_ZoneMinderPrimary go2rtc stream. However, that stream is only
registered when RTSPServer is enabled on the monitor. Fall back
to _CameraDirectPrimary when RTSPServer is not enabled.
Pass RTSPServer property through monitorData in all views so the
JS can check it at stream selection time.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
start() reconstructs the zms URL from url_to_zms, which only contains
the monitor id. Parameters like auth, connkey, scale, and mode are
carried over, but maxfps was dropped. This causes streams to start at
the camera's native capture rate even when PHP rendered the initial
<img src> with a maxfps value (e.g. from the montage Rate cookie).
Carry over maxfps from the existing src the same way the other
parameters are preserved.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The PHP status.php returns 'auth' but getStatusCmdResponse was checking
for 'auth_hash'. This mismatch prevented auth hash updates from status
responses from ever being applied, causing invalid hash errors after
the auth expired.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Added started flag checks in streamCommand() and streamCmdQuery() to
prevent commands from being sent after the stream has been killed.
This fixes an issue where scale commands were being sent after quit
commands during page reload.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Two issues caused monitors to stream from wrong channel:
1. In setChannelStream(), the expression `StreamChannel && SecondPath`
returned SecondPath (often empty) instead of StreamChannel. Now
correctly uses the monitor's configured StreamChannel.
2. In MonitorStream.start(), default channel only checked for
CameraDirectSecondary and defaulted everything else to Restream.
Now respects the actual StreamChannel setting.
Also improved fallback when selected option is disabled to find first
enabled option rather than always defaulting to Restream.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Rename applies to Go2RTC, Janus, and RTSP2Web streaming options.
Update enum values from Primary/Secondary to Restream/CameraDirectPrimary/CameraDirectSecondary.
- Add db migration zm_update-1.37.79.sql to rename column and migrate data
- Update C++ enum StreamChannelOption and member stream_channel
- Update PHP getStreamChannelOptions() method
- Update all JavaScript references
- Auto-select CameraDirectPrimary when Restream option becomes disabled
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
It's not entirely clear why it was necessary to change the "this.controlMute" function in the commit.f796fa913f
After this change, the "volumeSlider" and "iconMute" styling sometimes doesn't work correctly.
The old code was 99.9% debugged and worked without any issues.
It's also unclear why "this.muted" was added? After all, we already had "audioStream.muted." Now we're probably getting duplicates.