Two spots wrote temp files outside ZoneMinder's controlled temp tree:
- zmvideo.pl concat mode built its ffmpeg concat list at a predictable
path, /tmp/<concat_name>.concat.lst, in world-writable /tmp. A
predictable name there is open to a symlink/race and leaks monitor and
event names. Create it instead with File::Temp (randomized name, atomic
O_EXCL) inside ZM_TMPDIR. The list entries are absolute paths, so its
location does not affect ffmpeg's resolution.
- web/ajax/training.php created its detection scratch image with
tempnam(sys_get_temp_dir(), ...), escaping ZM's temp tree and its
cleanup. Use tempnam(ZM_DIR_TEMP, ...) so it stays under the configured
temp dir.
Both now resolve to the per-distro temp dir (e.g. /var/lib/zoneminder/temp
on RedHat, /var/tmp/zm on Debian), keeping scratch files inside the tree
that packaging and systemd hardening already cover.
refs #2915
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Split the config value on the first whitespace so the executable path
is validated separately and extra arguments (e.g. -c config.yml) are
passed through to the command line.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Rebuild data.yaml when deleting annotations (delete and browse_delete
actions) so unused class names are removed and IDs re-indexed
- Extract rebuildDataYaml() helper, replacing duplicated inline rebuild
logic in browse_delete
- Make sidebar trash button always visible for recovery from inconsistent
states; _deleteAllTrainingData now also clears editor annotations
- Consolidate permission/POST checks into arrays before switch statement
- Extract helper functions: resolveTrainingPath, getFrameStem,
getFramePaths, _loadEventData, _confirmDiscardIfDirty,
_resetAnnotationState, _applyEventData, _getNormalizedRect
- Extract 8 pure browse utility functions to module level
- Add floatval() validation on bbox coordinates in save action
- Add LOCK_EX to writeDataYaml, add .fail(logAjaxFail) to AJAX calls
- Replace var with let/const, add 'use strict', remove IIFEs
- CSS custom properties for repeated values, requestAnimationFrame
throttle for canvas mousemove
- Align with ZM conventions: error messages include username/action,
trailing ajaxError after switch, "use strict" at line 1
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add browse_objects PHP action that groups annotated images by class
label and collects background images separately. Inject a virtual
"Objects" folder at the top of the browse tree with fa-tags icon.
Expanding it shows per-class sub-items with count badges and a
background images category. Clicking a class displays a thumbnail
grid; clicking a thumbnail navigates to the annotation editor for
that event/frame.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add load_saved PHP action that reads YOLO label files and converts
normalized coordinates back to pixels. When navigating to an event
via the browse edit button, saved annotations are loaded into the
editor so users can review and modify previous work.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Change default training data directory from @ZM_CACHEDIR@/training to
@ZM_CONTENTDIR@/training so it lives alongside events storage instead
of inside the web cache folder. Update PHP fallback to use
dirname(ZM_DIR_EVENTS) accordingly.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix path traversal via directory prefix matching: append
DIRECTORY_SEPARATOR to base path before strpos containment check
(2 locations in training.php)
- Fix temp file leak: rename tempnam() base file to .jpg instead of
creating a second file, leaving the original orphaned
- Remove raw_output from detect response (information disclosure)
- Fix double _pushUndo on Delete key (keydown handler + deleteAnnotation)
- Fix help text: "alongside events storage" → "inside the ZoneMinder
cache directory" to match actual ZM_DIR_CACHE default (2 locations)
- Add missing .frame-total span element in event.php
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace beforeunload with click-capture nav guard to avoid blank
screen caused by ZM's global beforeunload content-hide animation
- Wrap streamPrev/streamNext to prompt before stream teardown
- Warn when saving with unaccepted (orange) detection boxes
- Add PHP fallback for empty TRAINING_DATA_DIR using ZM_DIR_CACHE
- Show spinning loader overlay with blur while frame images load
- Change detection running status to blue (info) color
- Fix label picker uppercase caused by ZM global button CSS
- Add TrainingPendingOnly, TrainingPendingDiscard translation keys
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Rename migration to zm_update-1.39.1.sql, add DEALLOCATE PREPARE,
fix Requires format to 'ZM_OPT_TRAINING=1'
- Bump version.txt to 1.39.1
- Remove docs/plans/ and utils/deploy-training.sh from repo
- training.php: remove PHP fallback path, add validFrameId() helper,
move buildTree() to top-level with symlink skip and depth limit,
split canView/canEdit permissions, require POST for destructive ops,
remove path disclosure, sanitize log output, add is_executable check
- training.js: remove console.log calls, fix _setStatus boolean to
string type, fix double undo push, switch delete ops to POST,
replace hardcoded English strings with translation keys
- event.js.php: remove window.annotationEditor global, add new
translation keys to trainingTranslations
- event.php: add frame skip and browse buttons, break up long HTML
- en_gb.php: add missing translation keys in alphabetical order
- training.css: add frame browse overlay and pagination styles
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Rewrite browse overlay as two-panel layout: directory tree (left) + file listing/preview (right)
- Add browse_file PHP action to serve images and text files from training dir
- Add browse_delete PHP action with class ID remapping to keep data.yaml consistent
- Overlay bounding boxes on image previews from corresponding label files
- Add per-file delete button (trash icon) that removes both image and label pair
- Move delete-all trash icon to sidebar header, status messages below sidebar
- Use existing ZM utilities: human_filesize() for file sizes, detaintPath() for path security
- Add SelectBoxFirst translation for delete-without-selection feedback
- Fix orphaned class ID bug: remap IDs in all label files when classes are removed
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add Annotate button to objdetect modal (events list) with auto-open
via ?annotate=1 URL parameter
- Compact stats layout: label and value on same row, trash icon to
delete all training data with typed confirmation prompt
- Add delete_all AJAX action to training.php
- Reduce all annotation panel font sizes to 0.8rem to match ZM base
- Fix label picker white-on-white by setting explicit colors
- Add beforeunload guard for unsaved annotations
- Switch detect action from shell_exec to exec for better error handling
- Show "no objects detected" as info status, not error
- Increase status message display time to 10 seconds
- Add i18n strings: ConfirmDeleteTrainingData, DeleteAllTrainingData,
TrainingDataDeleted
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove empty annotations rejection from PHP save action
- Empty label file = background/negative image in YOLO format
- JS prompts user when saving with no objects: "Save as background
image? Background images help reduce false positives."
- Stats now show background image count separately
- Delete Box button already works on selected pending boxes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Was hardcoded to %06d but the actual padding depends on the
ZM_EVENT_IMAGE_DIGITS config (typically 5).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add ZM_TRAINING_DETECT_SCRIPT config option for specifying the path
to an external detection script (e.g. zm_detect.py). When configured,
a Detect button appears in the annotation editor that:
- Runs the script with -f <image> -m <monitor_id>
- Parses the --SPLIT-- JSON output for labels, boxes, confidences
- Displays results as orange (pending) bounding boxes
- Users can accept (checkmark) or reject (x) each detection
- Only accepted annotations are saved to the training set
Also adds accept/reject UI in the sidebar object list, with orange
color for pending detections and normal colors for accepted ones.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove objdetect from frame selector and fid validation
- Fix 500 error: replace non-existent ZM\AuditAction() with ZM\Info()
- Scale canvas text/borders/handles relative to canvas width for readability
- Change annotate button icon from fa-pencil-square-o to fa-crosshairs
- Bump CSS font sizes from 0.75-0.9rem to 0.95-1rem
- Use orange (#ff8c00) stroke for in-progress bounding box drawing
- Flash save button green (btn-saved class) on successful save
- Hide #eventVideo when annotation panel is open
- Skip empty label files in getTrainingStats() count
- Reject save with empty annotations array
- Add undo support for delete and AJAX delete when all boxes removed
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add canvas-based bounding box annotation editor to the event view for
correcting object detection results and building YOLO training datasets.
- Two new config options: ZM_OPT_TRAINING (toggle) and ZM_TRAINING_DATA_DIR
- AJAX backend (training.php) with load/save/delete/status actions
- Canvas annotation editor (training.js) with draw/resize/relabel/undo
- Frame navigation (alarm/snapshot/objdetect/numbered frames)
- Roboflow-compatible YOLO output (images/all/, labels/all/, data.yaml)
- Training data statistics with per-class image counts and guidance
- Full i18n support via en_gb.php SLANG/OLANG entries
- Label validation, YOLO coordinate clamping, audit logging
- DB migration for existing installs (zm_update-1.39.2.sql)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>