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>
When zone coordinates are stored as pixel values (e.g. from a missed DB
migration), the web layer now detects values > 100 and converts them to
percentages using the monitor's dimensions, mirroring the existing C++
detection logic in zm_zone.cpp. This prevents limitPoints() from clamping
pixel values to 0-100 and zones rendering incorrectly in SVG overlays.
- Add convertPixelPointsToPercent() helper in functions.php
- Call conversion before limitPoints() in zone.php and zones.php
- Update Zone::svg_polygon() to accept monitor dimensions and convert
- Pass ViewWidth/ViewHeight to svg_polygon() from Monitor::getStreamHTML()
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace img load event listener with ResizeObserver on imageFeed
container. The old approach listened for load events on #imageFrame img
elements, which never fires for video-based streams (Go2RTC MSE) since
they use <video> not <img>. ResizeObserver fires whenever the container
dimensions change regardless of content type, fixing incorrect zone
point positions on initial load.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
On the Zones page, with the default player = go2rtc, a zone may not display due to the added <video-stream> tag
due to "position: relative"
And zones should always be above all other elements.
Possible fix#4648
- 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>
Add RBAC checks to ConfigsController edit() and delete() requiring
System=Edit permission, matching the pattern used by other controllers.
Harden System/Readonly column checks with !empty() to handle missing
columns gracefully. Fix command injection in Event.php by using
ZM_PATH_FFMPEG constant with escapeshellarg() instead of hardcoded
unsanitized ffmpeg call. Add is_executable() validation at all exec()
sites using ZM_PATH_FFMPEG as defense-in-depth against poisoned config
values.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
setCookie('speed', ...) was storing the speedIndex (array position)
but the PHP side reads the cookie and compares it against speed values
to find the matching index. Storing the index meant the value never
matched, so the slider always reset to the default 1x on reload.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fix FFmpeg initial seek overshooting to a future keyframe when the
requested timestamp falls between keyframes. After the initial
AVSEEK_FLAG_FRAME seek, detect if the returned frame is past the
target and re-seek backward to get the correct keyframe before it.
On the JS side, preserve fractional seconds throughout the playback
pipeline: remove Math.floor() truncation in mmove() and setSpeed(),
and use parseFloat instead of parseInt for currentTimeSecs
initialization. Reduce initial display interval from 1000ms to 100ms
for ~10fps refresh rate during review playback.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add data-monitor-width and data-monitor-height attributes to thumbnail
img elements in console.php, events.php, watch.php, and monitor.php.
Use these to calculate the scale parameter as a ratio of overlay size to
monitor native resolution, clamped to 5-100%, instead of hardcoding
scale=75 (overlay) and scale=32 (fallback).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fix monitor config view labels being pushed off-screen to the left on
mobile. The CSS specificity of the mobile override selector
(ul.form > li > label) was lower than the base rule
(ul.form > li > label:first-child), so margin-left: -300px was never
overridden on small screens. Match the :first-child pseudo-class in
the media query to fix.
Also add mobile responsive overrides for the watch view (which had
none), and change flex-nowrap to flex-md-nowrap so sidebar/PTZ stack
vertically on phones.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a new AUDIT logging level (-5) between PANIC (-4) and NOLOG (shifted
to -6) across C++, PHP, and Perl loggers. AUDIT entries use code 'AUD'
and syslog priority LOG_NOTICE. They record who changed what, from where,
for monitors, filters, users, config, roles, groups, zones, states,
servers, storage, events, snapshots, control caps, and login/logout.
AUDIT entries have their own retention period (ZM_LOG_AUDIT_DATABASE_LIMIT,
default 1 year) separate from regular log pruning. The log pruning in
zmstats.pl and zmaudit.pl now excludes AUDIT rows from regular pruning
and prunes them independently.
Critical safety: the C++ termination logic is changed from
'if (level <= FATAL)' to 'if (level == FATAL || level == PANIC)' to
prevent AUDIT-level log calls from killing the process.
Includes db migration zm_update-1.39.1.sql to shift any stored NOLOG
config values from -5 to -6.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Convert zone coordinates from absolute pixel values to percentages
(0.00-100.00) so zones automatically adapt when monitor resolution
changes. This eliminates the need to manually reconfigure zones after
resolution adjustments.
Changes:
- Add DB migration (zm_update-1.37.81.sql) to convert existing pixel
coords to percentages, recalculate area, and update Units default
- Add Zone::ParsePercentagePolygon() in C++ to parse percentage coords
and convert to pixels at runtime using monitor dimensions
- Backwards compat: C++ Zone::Load() checks Units column and uses old
pixel parser for legacy 'Pixels' zones
- Update PHP coordsToPoints/mapCoords/getPolyArea for float coords,
replace scanline area algorithm with shoelace formula
- Update JS zone editor to work in percentage coordinate space with
SVG viewBox "0 0 100 100" and non-scaling-stroke for consistent
line thickness
- Position zone SVG overlay inside imageFeed container via JS to align
with image only (not status bar)
- Support array of zone IDs in Monitor::getStreamHTML zones option
- Update monitor resize handler: percentage coords don't need rescaling,
only threshold pixel counts are adjusted
- Add 8 Catch2 unit tests for ParsePercentagePolygon
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update URL when filter changes so the filter state can be shared via URL
and browser back/forward navigation restores previous filter states.
- Add buildFilterQuery() helper to construct filter query string
- Add history.replaceState() on page load for initial state
- Add history.pushState() in filterEvents() when filters change
- Add popstate event listener to restore filter state on back/forward
- Refactor filterEvents() to accept options parameter for skipPushState
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
document.getElementById('evtStream') can return null if the element
was removed from the DOM during event navigation or deletion, causing
a TypeError when accessing .src on the result.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>